From 5559930eabc0bb064f7a1f79eb98ea0d99562b1e Mon Sep 17 00:00:00 2001 From: David Li Date: Thu, 23 Jan 2025 23:45:20 -0500 Subject: [PATCH] GH-546: Migrate Sphinx documentation (#553) Fixes #546. --------- Co-authored-by: Sutou Kouhei --- .github/workflows/rc.yml | 47 +- .gitignore | 1 + dev/release/rat_exclude_files.txt | 1 + docs/Makefile | 20 + docs/README.md | 28 + docs/requirements.txt | 28 + docs/source/_static/.gitignore | 0 docs/source/algorithm.rst | 92 +++ docs/source/cdata.rst | 468 +++++++++++++ docs/source/conf.py | 51 ++ docs/source/dataset.rst | 309 +++++++++ docs/source/developers/building.rst | 624 ++++++++++++++++++ docs/source/developers/development.rst | 197 ++++++ .../developers/img/conbench_benchmark.png | Bin 0 -> 141075 bytes docs/source/developers/img/conbench_runs.png | Bin 0 -> 62681 bytes docs/source/developers/img/conbench_ui.png | Bin 0 -> 70336 bytes docs/source/developers/index.rst | 28 + docs/source/flight.rst | 239 +++++++ docs/source/flight_sql.rst | 32 + docs/source/flight_sql_jdbc_driver.rst | 175 +++++ docs/source/index.rst | 48 ++ docs/source/install.rst | 230 +++++++ docs/source/ipc.rst | 202 ++++++ docs/source/jdbc.rst | 278 ++++++++ docs/source/memory.rst | 499 ++++++++++++++ docs/source/overview.rst | 90 +++ docs/source/quickstartguide.rst | 314 +++++++++ docs/source/reference/index.rst | 21 + docs/source/substrait.rst | 201 ++++++ docs/source/table.rst | 378 +++++++++++ docs/source/vector.rst | 366 ++++++++++ docs/source/vector_schema_root.rst | 163 +++++ 32 files changed, 5126 insertions(+), 4 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/requirements.txt create mode 100644 docs/source/_static/.gitignore create mode 100644 docs/source/algorithm.rst create mode 100644 docs/source/cdata.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/dataset.rst create mode 100644 docs/source/developers/building.rst create mode 100644 docs/source/developers/development.rst create mode 100644 docs/source/developers/img/conbench_benchmark.png create mode 100644 docs/source/developers/img/conbench_runs.png create mode 100644 docs/source/developers/img/conbench_ui.png create mode 100644 docs/source/developers/index.rst create mode 100644 docs/source/flight.rst create mode 100644 docs/source/flight_sql.rst create mode 100644 docs/source/flight_sql_jdbc_driver.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/install.rst create mode 100644 docs/source/ipc.rst create mode 100644 docs/source/jdbc.rst create mode 100644 docs/source/memory.rst create mode 100644 docs/source/overview.rst create mode 100644 docs/source/quickstartguide.rst create mode 100644 docs/source/reference/index.rst create mode 100644 docs/source/substrait.rst create mode 100644 docs/source/table.rst create mode 100644 docs/source/vector.rst create mode 100644 docs/source/vector_schema_root.rst diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml index 61281964..9dc04fee 100644 --- a/.github/workflows/rc.yml +++ b/.github/workflows/rc.yml @@ -421,8 +421,8 @@ jobs: - name: Prepare docs run: | mkdir -p docs - cp -a target/site/apidocs docs/reference - tar -cvzf docs.tar.gz docs + cp -a target/site/apidocs reference + tar -cvzf reference.tar.gz reference - name: Upload binaries uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: @@ -431,8 +431,46 @@ jobs: - name: Upload docs uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: - name: release-docs - path: docs.tar.gz + name: reference + path: reference.tar.gz + docs: + name: Docs + needs: + - binaries + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + with: + cache: 'pip' + - name: Download source archive + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: release-source + - name: Download Javadocs + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: reference + - name: Extract source archive + run: | + tar -xf apache-arrow-java-*.tar.gz --strip-components=1 + - name: Build + run: | + cd docs + python -m venv venv + source venv/bin/activate + pip install -r requirements.txt + make html + tar -xf ../reference.tar.gz -C build/html + - name: Compress into single artifact to keep directory structure + run: tar -cvzf html.tar.gz -C docs/build html + - name: Upload artifacts + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: release-html + path: html.tar.gz verify: name: Verify needs: @@ -473,6 +511,7 @@ jobs: name: Upload if: github.ref_type == 'tag' needs: + - docs - verify runs-on: ubuntu-latest permissions: diff --git a/.gitignore b/.gitignore index 205be77e..b57597af 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ /dev/release/apache-rat-0.16.1.jar /dev/release/filtered_rat.txt /dev/release/rat.xml +/docs/build/ CMakeCache.txt CMakeFiles/ Makefile diff --git a/dev/release/rat_exclude_files.txt b/dev/release/rat_exclude_files.txt index 8efd379a..8324d32c 100644 --- a/dev/release/rat_exclude_files.txt +++ b/dev/release/rat_exclude_files.txt @@ -17,3 +17,4 @@ .gitmodules dataset/src/test/resources/data/student.csv +docs/Makefile diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..a4de0bff --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= -W +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..70c2ef2a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ + + +# Documentation + +Build with Sphinx. + +```bash +cd docs +pip install -r requirements.txt +make html +``` diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..fa2d0bbe --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +furo==2024.8.6 +myst-parser==4.0.0 +Sphinx==8.1.3 +sphinx-autobuild==2024.10.3 +sphinx-basic-ng==1.0.0b2 +sphinxcontrib-applehelp==2.0.0 +sphinxcontrib-devhelp==2.0.0 +sphinxcontrib-htmlhelp==2.1.0 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==2.0.0 +sphinxcontrib-serializinghtml==2.0.0 diff --git a/docs/source/_static/.gitignore b/docs/source/_static/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/algorithm.rst b/docs/source/algorithm.rst new file mode 100644 index 00000000..d4838967 --- /dev/null +++ b/docs/source/algorithm.rst @@ -0,0 +1,92 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +Java Algorithms +=============== + +Arrow's Java library provides algorithms for some commonly-used +functionalities. The algorithms are provided in the ``org.apache.arrow.algorithm`` +package of the ``algorithm`` module. + +Comparing Vector Elements +------------------------- + +Comparing vector elements is the basic for many algorithms. Vector +elements can be compared in one of the two ways: + +1. **Equality comparison**: there are two possible results for this type of comparisons: ``equal`` and ``unequal``. +Currently, this type of comparison is supported through the ``org.apache.arrow.vector.compare.VectorValueEqualizer`` +interface. + +2. **Ordering comparison**: there are three possible results for this type of comparisons: ``less than``, ``equal to`` +and ``greater than``. This comparison is supported by the abstract class ``org.apache.arrow.algorithm.sort.VectorValueComparator``. + +We provide default implementations to compare vector elements. However, users can also define ways +for customized comparisons. + +Vector Element Search +--------------------- + +A search algorithm tries to find a particular value in a vector. When successful, a vector index is +returned; otherwise, a ``-1`` is returned. The following search algorithms are provided: + +1. **Linear search**: this algorithm simply traverses the vector from the beginning, until a match is +found, or the end of the vector is reached. So it takes ``O(n)`` time, where ``n`` is the number of elements +in the vector. This algorithm is implemented in ``org.apache.arrow.algorithm.search.VectorSearcher#linearSearch``. + +2. **Binary search**: this represents a more efficient search algorithm, as it runs in ``O(log(n))`` time. +However, it is only applicable to sorted vectors. To get a sorted vector, +one can use one of our sorting algorithms, which will be discussed in the next section. This algorithm +is implemented in ``org.apache.arrow.algorithm.search.VectorSearcher#binarySearch``. + +3. **Parallel search**: when the vector is large, it takes a long time to traverse the elements to search +for a value. To make this process faster, one can split the vector into multiple partitions, and perform the +search for each partition in parallel. This is supported by ``org.apache.arrow.algorithm.search.ParallelSearcher``. + +4. **Range search**: for many scenarios, there can be multiple matching values in the vector. +If the vector is sorted, the matching values reside in a contiguous region in the vector. The +range search algorithm tries to find the upper/lower bound of the region in ``O(log(n))`` time. +An implementation is provided in ``org.apache.arrow.algorithm.search.VectorRangeSearcher``. + +Vector Sorting +-------------- + +Given a vector, a sorting algorithm turns it into a sorted one. The sorting criteria must +be specified by some ordering comparison operation. The sorting algorithms can be +classified into the following categories: + +1. **In-place sorter**: an in-place sorter performs the sorting by manipulating the original +vector, without creating any new vector. So it just returns the original vector after the sorting operations. +Currently, we have ``org.apache.arrow.algorithm.sort.FixedWidthInPlaceVectorSorter`` for in-place +sorting in ``O(nlog(n))`` time. As the name suggests, it only supports fixed width vectors. + +2. **Out-of-place sorter**: an out-of-place sorter does not mutate the original vector. Instead, +it copies vector elements to a new vector in sorted order, and returns the new vector. +We have ``org.apache.arrow.algorithm.sort.FixedWidthInPlaceVectorSorter.FixedWidthOutOfPlaceVectorSorter`` +and ``org.apache.arrow.algorithm.sort.FixedWidthInPlaceVectorSorter.VariableWidthOutOfPlaceVectorSorter`` +for fixed width and variable width vectors, respectively. Both algorithms run in ``O(nlog(n))`` time. + +3. **Index sorter**: this sorter does not actually sort the vector. Instead, it returns an integer +vector, which correspond to indices of vector elements in sorted order. With the index vector, one can +easily construct a sorted vector. In addition, some other tasks can be easily achieved, like finding the ``k`` th +smallest value in the vector. Index sorting is supported by ``org.apache.arrow.algorithm.sort.IndexSorter``, +which runs in ``O(nlog(n))`` time. It is applicable to vectors of any type. + +Other Algorithms +---------------- + +Other algorithms include vector deduplication, dictionary encoding, etc., in the ``algorithm`` module. diff --git a/docs/source/cdata.rst b/docs/source/cdata.rst new file mode 100644 index 00000000..9643d88d --- /dev/null +++ b/docs/source/cdata.rst @@ -0,0 +1,468 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +================ +C Data Interface +================ + +Arrow supports exchanging data without copying or serialization within the same process +through :external+arrow:ref:`c-data-interface`, even between different language runtimes. + +Java to Python +-------------- + +See :external+arrow:doc:`python/integration/python_java` to implement Java to +Python communication using the C Data Interface. + +Java to C++ +----------- + +See :external+arrow:doc:`developers/cpp/building` to build the Arrow C++ libraries: + +.. code-block:: shell + + $ git clone https://github.com/apache/arrow.git + $ cd arrow/cpp + $ mkdir build # from inside the `cpp` subdirectory + $ cd build + $ cmake .. --preset ninja-debug-minimal + $ cmake --build . + $ tree debug/ + debug/ + ├── libarrow.800.0.0.dylib + ├── libarrow.800.dylib -> libarrow.800.0.0.dylib + └── libarrow.dylib -> libarrow.800.dylib + +Share an Int64 array from C++ to Java +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**C++ Side** + +Implement a function in CDataCppBridge.h that exports an array via the C Data Interface: + +.. code-block:: cpp + + #include + #include + #include + + void FillInt64Array(const uintptr_t c_schema_ptr, const uintptr_t c_array_ptr) { + arrow::Int64Builder builder; + builder.Append(1); + builder.Append(2); + builder.Append(3); + builder.AppendNull(); + builder.Append(5); + builder.Append(6); + builder.Append(7); + builder.Append(8); + builder.Append(9); + builder.Append(10); + std::shared_ptr array = *builder.Finish(); + + struct ArrowSchema* c_schema = reinterpret_cast(c_schema_ptr); + auto c_schema_status = arrow::ExportType(*array->type(), c_schema); + if (!c_schema_status.ok()) c_schema_status.Abort(); + + struct ArrowArray* c_array = reinterpret_cast(c_array_ptr); + auto c_array_status = arrow::ExportArray(*array, c_array); + if (!c_array_status.ok()) c_array_status.Abort(); + } + +**Java Side** + +For this example, we will use `JavaCPP`_ to call our C++ function from Java, +without writing JNI bindings ourselves. + +.. code-block:: xml + + + + 4.0.0 + + org.example + java-cdata-example + 1.0-SNAPSHOT + + + 8 + 8 + 9.0.0 + + + + org.bytedeco + javacpp + 1.5.7 + + + org.apache.arrow + arrow-c-data + ${arrow.version} + + + org.apache.arrow + arrow-vector + ${arrow.version} + + + org.apache.arrow + arrow-memory-core + ${arrow.version} + + + org.apache.arrow + arrow-memory-netty + ${arrow.version} + + + org.apache.arrow + arrow-format + ${arrow.version} + + + + +.. code-block:: java + + import org.bytedeco.javacpp.annotation.Platform; + import org.bytedeco.javacpp.annotation.Properties; + import org.bytedeco.javacpp.tools.InfoMap; + import org.bytedeco.javacpp.tools.InfoMapper; + + @Properties( + target = "CDataJavaToCppExample", + value = @Platform( + include = { + "CDataCppBridge.h" + }, + compiler = {"cpp17"}, + linkpath = {"/arrow/cpp/build/debug/"}, + link = {"arrow"} + ) + ) + public class CDataJavaConfig implements InfoMapper { + + @Override + public void map(InfoMap infoMap) { + } + } + +.. code-block:: shell + + # Compile our Java code + $ javac -cp javacpp-1.5.7.jar CDataJavaConfig.java + + # Generate CDataInterfaceLibrary + $ java -jar javacpp-1.5.7.jar CDataJavaConfig.java + + # Generate libjniCDataInterfaceLibrary.dylib + $ java -jar javacpp-1.5.7.jar CDataJavaToCppExample.java + + # Validate libjniCDataInterfaceLibrary.dylib created + $ otool -L macosx-x86_64/libjniCDataJavaToCppExample.dylib + macosx-x86_64/libjniCDataJavaToCppExample.dylib: + libjniCDataJavaToCppExample.dylib (compatibility version 0.0.0, current version 0.0.0) + @rpath/libarrow.800.dylib (compatibility version 800.0.0, current version 800.0.0) + /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1200.3.0) + /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.0.0) + +**Java Test** + +Let's create a Java class to test our bridge: + +.. code-block:: java + + import org.apache.arrow.c.ArrowArray; + import org.apache.arrow.c.ArrowSchema; + import org.apache.arrow.c.Data; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.BigIntVector; + + public class TestCDataInterface { + public static void main(String[] args) { + try( + BufferAllocator allocator = new RootAllocator(); + ArrowSchema arrowSchema = ArrowSchema.allocateNew(allocator); + ArrowArray arrowArray = ArrowArray.allocateNew(allocator) + ){ + CDataJavaToCppExample.FillInt64Array( + arrowSchema.memoryAddress(), arrowArray.memoryAddress()); + try( + BigIntVector bigIntVector = (BigIntVector) Data.importVector( + allocator, arrowArray, arrowSchema, null) + ){ + System.out.println("C++-allocated array: " + bigIntVector); + } + } + } + } + +.. code-block:: shell + + C++-allocated array: [1, 2, 3, null, 5, 6, 7, 8, 9, 10] + +Share an Int32 array from Java to C++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Java Side** + +For this example, we will build a JAR with all dependencies bundled. + +.. code-block:: xml + + + + 4.0.0 + org.example + cpptojava + 1.0-SNAPSHOT + + 8 + 8 + 9.0.0 + + + + org.apache.arrow + arrow-c-data + ${arrow.version} + + + org.apache.arrow + arrow-memory-netty + ${arrow.version} + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + jar-with-dependencies + + + + + + + + + +.. code-block:: java + + import org.apache.arrow.c.ArrowArray; + import org.apache.arrow.c.ArrowSchema; + import org.apache.arrow.c.Data; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.FieldVector; + import org.apache.arrow.vector.IntVector; + import org.apache.arrow.vector.VectorSchemaRoot; + + import java.util.Arrays; + + public class ToBeCalledByCpp { + final static BufferAllocator allocator = new RootAllocator(); + + /** + * Create a {@link FieldVector} and export it via the C Data Interface + * @param schemaAddress Schema memory address to wrap + * @param arrayAddress Array memory address to wrap + */ + public static void fillVector(long schemaAddress, long arrayAddress){ + try (ArrowArray arrow_array = ArrowArray.wrap(arrayAddress); + ArrowSchema arrow_schema = ArrowSchema.wrap(schemaAddress) ) { + Data.exportVector(allocator, populateFieldVectorToExport(), null, arrow_array, arrow_schema); + } + } + + /** + * Create a {@link VectorSchemaRoot} and export it via the C Data Interface + * @param schemaAddress Schema memory address to wrap + * @param arrayAddress Array memory address to wrap + */ + public static void fillVectorSchemaRoot(long schemaAddress, long arrayAddress){ + try (ArrowArray arrow_array = ArrowArray.wrap(arrayAddress); + ArrowSchema arrow_schema = ArrowSchema.wrap(schemaAddress) ) { + Data.exportVectorSchemaRoot(allocator, populateVectorSchemaRootToExport(), null, arrow_array, arrow_schema); + } + } + + private static FieldVector populateFieldVectorToExport(){ + IntVector intVector = new IntVector("int-to-export", allocator); + intVector.allocateNew(3); + intVector.setSafe(0, 1); + intVector.setSafe(1, 2); + intVector.setSafe(2, 3); + intVector.setValueCount(3); + System.out.println("[Java] FieldVector: \n" + intVector); + return intVector; + } + + private static VectorSchemaRoot populateVectorSchemaRootToExport(){ + IntVector intVector = new IntVector("age-to-export", allocator); + intVector.setSafe(0, 10); + intVector.setSafe(1, 20); + intVector.setSafe(2, 30); + VectorSchemaRoot root = new VectorSchemaRoot(Arrays.asList(intVector)); + root.setRowCount(3); + System.out.println("[Java] VectorSchemaRoot: \n" + root.contentToTSVString()); + return root; + } + } + +Build the JAR and copy it to the C++ project. + +.. code-block:: shell + + $ mvn clean install + $ cp target/cpptojava-1.0-SNAPSHOT-jar-with-dependencies.jar /cpptojava.jar + +**C++ Side** + +This application uses JNI to call Java code, but transfers data (zero-copy) via the C Data Interface instead. + +.. code-block:: cpp + + #include + #include + + #include + #include + + JNIEnv *CreateVM(JavaVM **jvm) { + JNIEnv *env; + JavaVMInitArgs vm_args; + JavaVMOption options[2]; + options[0].optionString = "-Djava.class.path=cpptojava.jar"; + options[1].optionString = "-DXcheck:jni:pedantic"; + vm_args.version = JNI_VERSION_10; + vm_args.nOptions = 2; + vm_args.options = options; + int status = JNI_CreateJavaVM(jvm, (void **) &env, &vm_args); + if (status < 0) { + std::cerr << "\n<<<<< Unable to Launch JVM >>>>>\n" << std::endl; + return nullptr; + } + return env; + } + + int main() { + JNIEnv *env; + JavaVM *jvm; + env = CreateVM(&jvm); + if (env == nullptr) return EXIT_FAILURE; + jclass javaClassToBeCalledByCpp = env->FindClass("ToBeCalledByCpp"); + if (javaClassToBeCalledByCpp != nullptr) { + jmethodID fillVector = env->GetStaticMethodID(javaClassToBeCalledByCpp, + "fillVector", + "(JJ)V"); + if (fillVector != nullptr) { + struct ArrowSchema arrowSchema; + struct ArrowArray arrowArray; + std::cout << "\n<<<<< C++ to Java for Arrays >>>>>\n" << std::endl; + env->CallStaticVoidMethod(javaClassToBeCalledByCpp, fillVector, + static_cast(reinterpret_cast(&arrowSchema)), + static_cast(reinterpret_cast(&arrowArray))); + auto resultImportArray = arrow::ImportArray(&arrowArray, &arrowSchema); + std::shared_ptr array = resultImportArray.ValueOrDie(); + std::cout << "[C++] Array: " << array->ToString() << std::endl; + } else { + std::cerr << "Could not find fillVector method\n" << std::endl; + return EXIT_FAILURE; + } + jmethodID fillVectorSchemaRoot = env->GetStaticMethodID(javaClassToBeCalledByCpp, + "fillVectorSchemaRoot", + "(JJ)V"); + if (fillVectorSchemaRoot != nullptr) { + struct ArrowSchema arrowSchema; + struct ArrowArray arrowArray; + std::cout << "\n<<<<< C++ to Java for RecordBatch >>>>>\n" << std::endl; + env->CallStaticVoidMethod(javaClassToBeCalledByCpp, fillVectorSchemaRoot, + static_cast(reinterpret_cast(&arrowSchema)), + static_cast(reinterpret_cast(&arrowArray))); + auto resultImportVectorSchemaRoot = arrow::ImportRecordBatch(&arrowArray, &arrowSchema); + std::shared_ptr recordBatch = resultImportVectorSchemaRoot.ValueOrDie(); + std::cout << "[C++] RecordBatch: " << recordBatch->ToString() << std::endl; + } else { + std::cerr << "Could not find fillVectorSchemaRoot method\n" << std::endl; + return EXIT_FAILURE; + } + } else { + std::cout << "Could not find ToBeCalledByCpp class\n" << std::endl; + return EXIT_FAILURE; + } + jvm->DestroyJavaVM(); + return EXIT_SUCCESS; + } + +CMakeLists.txt definition file: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.19) + project(cdatacpptojava) + find_package(JNI REQUIRED) + find_package(Arrow REQUIRED) + message(STATUS "Arrow version: ${ARROW_VERSION}") + include_directories(${JNI_INCLUDE_DIRS}) + set(CMAKE_CXX_STANDARD 17) + add_executable(${PROJECT_NAME} main.cpp) + target_link_libraries(cdatacpptojava PRIVATE Arrow::arrow_shared) + target_link_libraries(cdatacpptojava PRIVATE ${JNI_LIBRARIES}) + +**Result** + +.. code-block:: text + + <<<<< C++ to Java for Arrays >>>>> + [Java] FieldVector: + [1, 2, 3] + [C++] Array: [ + 1, + 2, + 3 + ] + + <<<<< C++ to Java for RecordBatch >>>>> + [Java] VectorSchemaRoot: + age-to-export + 10 + 20 + 30 + + [C++] RecordBatch: age-to-export: [ + 10, + 20, + 30 + ] + +.. _`JavaCPP`: https://github.com/bytedeco/javacpp diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..166a3bc4 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,51 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'arrow-java' +copyright = '2025, Apache Arrow Developers' +author = 'Apache Arrow Developers' +release = '18.1.0' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = ["sphinx.ext.intersphinx"] + +templates_path = ['_templates'] +exclude_patterns = [] + +# -- Intersphinx ------------------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html + +intersphinx_mapping = { + 'arrow': ('https://arrow.apache.org/docs/', None), + 'cookbook': ('https://arrow.apache.org/cookbook/java/', None), +} + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'furo' +html_static_path = ['_static'] diff --git a/docs/source/dataset.rst b/docs/source/dataset.rst new file mode 100644 index 00000000..deaa0095 --- /dev/null +++ b/docs/source/dataset.rst @@ -0,0 +1,309 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +======= +Dataset +======= + +.. warning:: + + Experimental: The Java module ``dataset`` is currently under early + development. API might be changed in each release of Apache Arrow until it + gets mature. + +Dataset is an universal layer in Apache Arrow for querying data in different +formats or in different partitioning strategies. Usually the data to be queried +is supposed to be located from a traditional file system, however Arrow Dataset +is not designed only for querying files but can be extended to serve all +possible data sources such as from inter-process communication or from other +network locations, etc. + +Getting Started +=============== + +Currently supported file formats are: + +- Apache Arrow (``.arrow``) +- Apache ORC (``.orc``) +- Apache Parquet (``.parquet``) +- Comma-Separated Values (``.csv``) +- Line-delimited JSON Values (``.json``) + +Below shows a simplest example of using Dataset to query a Parquet file in Java: + +.. code-block:: Java + + // read data from file /opt/example.parquet + String uri = "file:/opt/example.parquet"; + ScanOptions options = new ScanOptions(/*batchSize*/ 32768); + try ( + BufferAllocator allocator = new RootAllocator(); + DatasetFactory datasetFactory = new FileSystemDatasetFactory( + allocator, NativeMemoryPool.getDefault(), + FileFormat.PARQUET, uri); + Dataset dataset = datasetFactory.finish(); + Scanner scanner = dataset.newScan(options); + ArrowReader reader = scanner.scanBatches() + ) { + List batches = new ArrayList<>(); + while (reader.loadNextBatch()) { + try (VectorSchemaRoot root = reader.getVectorSchemaRoot()) { + final VectorUnloader unloader = new VectorUnloader(root); + batches.add(unloader.getRecordBatch()); + } + } + + // do something with read record batches, for example: + analyzeArrowData(batches); + + // finished the analysis of the data, close all resources: + AutoCloseables.close(batches); + } catch (Exception e) { + e.printStackTrace(); + } + +.. note:: + ``ArrowRecordBatch`` is a low-level composite Arrow data exchange format + that doesn't provide API to read typed data from it directly. + It's recommended to use utilities ``VectorLoader`` to load it into a schema + aware container ``VectorSchemaRoot`` by which user could be able to access + decoded data conveniently in Java. + + The ``ScanOptions batchSize`` argument takes effect only if it is set to a value + smaller than the number of rows in the recordbatch. + +.. seealso:: + Load record batches with :doc:`VectorSchemaRoot `. + +Schema +====== + +Schema of the data to be queried can be inspected via method +``DatasetFactory#inspect()`` before actually reading it. For example: + +.. code-block:: Java + + // read data from local file /opt/example.parquet + String uri = "file:/opt/example.parquet"; + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + DatasetFactory factory = new FileSystemDatasetFactory(allocator, + NativeMemoryPool.getDefault(), FileFormat.PARQUET, uri); + + // inspect schema + Schema schema = factory.inspect(); + +For some of the data format that is compatible with a user-defined schema, user +can use method ``DatasetFactory#inspect(Schema schema)`` to create the dataset: + +.. code-block:: Java + + Schema schema = createUserSchema() + Dataset dataset = factory.finish(schema); + +Otherwise when the non-parameter method ``DatasetFactory#inspect()`` is called, +schema will be inferred automatically from data source. The same as the result +of ``DatasetFactory#inspect()``. + +Also, if projector is specified during scanning (see next section +:ref:`java-dataset-projection`), the actual schema of output data can be got +within method ``Scanner::schema()``: + +.. code-block:: Java + + Scanner scanner = dataset.newScan( + new ScanOptions(32768, Optional.of(new String[] {"id", "name"}))); + Schema projectedSchema = scanner.schema(); + +.. _java-dataset-projection: + +Projection (Subset of Columns) +============================== + +User can specify projections in ScanOptions. For example: + +.. code-block:: Java + + String[] projection = new String[] {"id", "name"}; + ScanOptions options = new ScanOptions(32768, Optional.of(projection)); + +If no projection is needed, leave the optional projection argument absent in +ScanOptions: + +.. code-block:: Java + + ScanOptions options = new ScanOptions(32768, Optional.empty()); + +Or use shortcut constructor: + +.. code-block:: Java + + ScanOptions options = new ScanOptions(32768); + +Then all columns will be emitted during scanning. + +Projection (Produce New Columns) and Filters +============================================ + +User can specify projections (new columns) or filters in ScanOptions using Substrait. For example: + +.. code-block:: Java + + ByteBuffer substraitExpressionFilter = getSubstraitExpressionFilter(); + ByteBuffer substraitExpressionProject = getSubstraitExpressionProjection(); + // Use Substrait APIs to create an Expression and serialize to a ByteBuffer + ScanOptions options = new ScanOptions.Builder(/*batchSize*/ 32768) + .columns(Optional.empty()) + .substraitExpressionFilter(substraitExpressionFilter) + .substraitExpressionProjection(getSubstraitExpressionProjection()) + .build(); + +.. seealso:: + + :doc:`Executing Projections and Filters Using Extended Expressions ` + Projections and Filters using Substrait. + +Read Data from HDFS +=================== + +``FileSystemDataset`` supports reading data from non-local file systems. HDFS +support is included in the official Apache Arrow Java package releases and +can be used directly without re-building the source code. + +To access HDFS data using Dataset API, pass a general HDFS URI to +``FilesSystemDatasetFactory``: + +.. code-block:: Java + + String uri = "hdfs://{hdfs_host}:{port}/data/example.parquet"; + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + DatasetFactory factory = new FileSystemDatasetFactory(allocator, + NativeMemoryPool.getDefault(), FileFormat.PARQUET, uri); + +Native Memory Management +======================== + +To gain better performance and reduce code complexity, Java +``FileSystemDataset`` internally relies on C++ +``arrow::dataset::FileSystemDataset`` via JNI. +As a result, all Arrow data read from ``FileSystemDataset`` is supposed to be +allocated off the JVM heap. To manage this part of memory, an utility class +``NativeMemoryPool`` is provided to users. + +As a basic example, by using a listenable ``NativeMemoryPool``, user can pass +a listener hooking on C++ buffer allocation/deallocation: + +.. code-block:: Java + + AtomicLong reserved = new AtomicLong(0L); + ReservationListener listener = new ReservationListener() { + @Override + public void reserve(long size) { + reserved.getAndAdd(size); + } + + @Override + public void unreserve(long size) { + reserved.getAndAdd(-size); + } + }; + NativeMemoryPool pool = NativeMemoryPool.createListenable(listener); + FileSystemDatasetFactory factory = new FileSystemDatasetFactory(allocator, + pool, FileFormat.PARQUET, uri); + + +Also, it's a very common case to reserve the same amount of JVM direct memory +for the data read from datasets. For this use a built-in utility +class ``DirectReservationListener`` is provided: + +.. code-block:: Java + + NativeMemoryPool pool = NativeMemoryPool.createListenable( + DirectReservationListener.instance()); + +This way, once the allocated byte count of Arrow buffers reaches the limit of +JVM direct memory, ``OutOfMemoryError: Direct buffer memory`` will +be thrown during scanning. + +.. note:: + The default instance ``NativeMemoryPool.getDefaultMemoryPool()`` does + nothing on buffer allocation/deallocation. It's OK to use it in + the case of POC or testing, but for production use in complex environment, + it's recommended to manage memory by using a listenable memory pool. + +.. note:: + The ``BufferAllocator`` instance passed to ``FileSystemDatasetFactory``'s + constructor is also aware of the overall memory usage of the produced + dataset instances. Once the Java buffers are created the passed allocator + will become their parent allocator. + +Usage Notes +=========== + +Native Object Resource Management +--------------------------------- + +As another result of relying on JNI, all components related to +``FileSystemDataset`` should be closed manually or use try-with-resources to +release the corresponding native objects after using. For example: + +.. code-block:: Java + + String uri = "file:/opt/example.parquet"; + ScanOptions options = new ScanOptions(/*batchSize*/ 32768); + try ( + BufferAllocator allocator = new RootAllocator(); + DatasetFactory factory = new FileSystemDatasetFactory( + allocator, NativeMemoryPool.getDefault(), + FileFormat.PARQUET, uri); + Dataset dataset = factory.finish(); + Scanner scanner = dataset.newScan(options) + ) { + + // do something + + } catch (Exception e) { + e.printStackTrace(); + } + +If user forgets to close them then native object leakage might be caused. + +BatchSize +--------- + +The ``batchSize`` argument of ``ScanOptions`` is a limit on the size of an individual batch. + +For example, let's try to read a Parquet file with gzip compression and 3 row groups: + +.. code-block:: + + # Let configure ScanOptions as: + ScanOptions options = new ScanOptions(/*batchSize*/ 32768); + + $ parquet-tools meta data4_3rg_gzip.parquet + file schema: schema + age: OPTIONAL INT64 R:0 D:1 + name: OPTIONAL BINARY L:STRING R:0 D:1 + row group 1: RC:4 TS:182 OFFSET:4 + row group 2: RC:4 TS:190 OFFSET:420 + row group 3: RC:3 TS:179 OFFSET:838 + +Here, we set the batchSize in ScanOptions to 32768. Because that's greater +than the number of rows in the next batch, which is 4 rows because the first +row group has only 4 rows, then the program gets only 4 rows. The scanner +will not combine smaller batches to reach the limit, but it will split +large batches to stay under the limit. So in the case the row group had more +than 32768 rows, it would get split into blocks of 32768 rows or less. diff --git a/docs/source/developers/building.rst b/docs/source/developers/building.rst new file mode 100644 index 00000000..f9ef7dae --- /dev/null +++ b/docs/source/developers/building.rst @@ -0,0 +1,624 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +.. highlight:: console + +.. _building-arrow-java: + +=================== +Building Arrow Java +=================== + +.. contents:: + +System Setup +============ + +Arrow Java uses the `Maven `_ build system. + +Building requires: + +* JDK 11+ +* Maven 3+ + +.. note:: + CI will test all supported JDK LTS versions, plus the latest non-LTS version. + +Building +======== + +All the instructions below assume that you have cloned the Arrow git +repository: + +.. code-block:: + + $ git clone https://github.com/apache/arrow.git + $ cd arrow + $ git submodule update --init --recursive + +These are the options available to compile Arrow Java modules with: + +* Maven build tool. +* Docker Compose. +* Archery. + +Building Java Modules +--------------------- + +To build the default modules, go to the project root and execute: + +Maven +~~~~~ + +.. code-block:: + + $ cd arrow/java + $ export JAVA_HOME= + $ java --version + $ mvn clean install + +Docker compose +~~~~~~~~~~~~~~ + +.. code-block:: + + $ cd arrow/java + $ export JAVA_HOME= + $ java --version + $ docker compose run java + +Archery +~~~~~~~ + +.. code-block:: + + $ cd arrow/java + $ export JAVA_HOME= + $ java --version + $ archery docker run java + +Building JNI Libraries (\*.dylib / \*.so / \*.dll) +-------------------------------------------------- + +First, we need to build the `C++ shared libraries`_ that the JNI bindings will use. +We can build these manually or we can use `Archery`_ to build them using a Docker container +(This will require installing Docker, Docker Compose, and Archery). + +.. note:: + If you are building on Apple Silicon, be sure to use a JDK version that was compiled + for that architecture. See, for example, the `Azul JDK `_. + + If you are building on Windows OS, see :ref:`Developing on Windows `. + +Maven +~~~~~ + +- To build only the JNI C Data Interface library (macOS / Linux): + + .. code-block:: text + + $ cd arrow/java + $ export JAVA_HOME= + $ java --version + $ mvn generate-resources -Pgenerate-libs-cdata-all-os -N + $ ls -latr ../java-dist/lib + |__ arrow_cdata_jni/ + +- To build only the JNI C Data Interface library (Windows): + + .. code-block:: + + $ cd arrow/java + $ mvn generate-resources -Pgenerate-libs-cdata-all-os -N + $ dir "../java-dist/bin" + |__ arrow_cdata_jni/ + +- To build all JNI libraries (macOS / Linux) except the JNI C Data Interface library: + + .. code-block:: text + + $ cd arrow/java + $ export JAVA_HOME= + $ java --version + $ mvn generate-resources -Pgenerate-libs-jni-macos-linux -N + $ ls -latr java-dist/lib + |__ arrow_dataset_jni/ + |__ arrow_orc_jni/ + |__ gandiva_jni/ + +- To build all JNI libraries (Windows) except the JNI C Data Interface library: + + .. code-block:: + + $ cd arrow/java + $ mvn generate-resources -Pgenerate-libs-jni-windows -N + $ dir "../java-dist/bin" + |__ arrow_dataset_jni/ + +CMake +~~~~~ + +- To build only the JNI C Data Interface library (macOS / Linux): + + .. code-block:: text + + $ cd arrow + $ mkdir -p java-dist java-cdata + $ cmake \ + -S java \ + -B java-cdata \ + -DARROW_JAVA_JNI_ENABLE_C=ON \ + -DARROW_JAVA_JNI_ENABLE_DEFAULT=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=java-dist + $ cmake --build java-cdata --target install --config Release + $ ls -latr java-dist/lib + |__ arrow_cdata_jni/ + +- To build only the JNI C Data Interface library (Windows): + + .. code-block:: text + + $ cd arrow + $ mkdir java-dist, java-cdata + $ cmake ^ + -S java ^ + -B java-cdata ^ + -DARROW_JAVA_JNI_ENABLE_C=ON ^ + -DARROW_JAVA_JNI_ENABLE_DEFAULT=OFF ^ + -DBUILD_TESTING=OFF ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_INSTALL_PREFIX=java-dist + $ cmake --build java-cdata --target install --config Release + $ dir "java-dist/bin" + |__ arrow_cdata_jni/ + +- To build all JNI libraries (macOS / Linux) except the JNI C Data Interface library: + + .. code-block:: text + + $ cd arrow + $ brew bundle --file=cpp/Brewfile + # Homebrew Bundle complete! 25 Brewfile dependencies now installed. + $ brew uninstall aws-sdk-cpp + # (We can't use aws-sdk-cpp installed by Homebrew because it has + # an issue: https://github.com/aws/aws-sdk-cpp/issues/1809 ) + $ export JAVA_HOME= + $ mkdir -p java-dist cpp-jni + $ cmake \ + -S cpp \ + -B cpp-jni \ + -DARROW_BUILD_SHARED=OFF \ + -DARROW_CSV=ON \ + -DARROW_DATASET=ON \ + -DARROW_DEPENDENCY_SOURCE=BUNDLED \ + -DARROW_DEPENDENCY_USE_SHARED=OFF \ + -DARROW_FILESYSTEM=ON \ + -DARROW_GANDIVA=ON \ + -DARROW_GANDIVA_STATIC_LIBSTDCPP=ON \ + -DARROW_JSON=ON \ + -DARROW_ORC=ON \ + -DARROW_PARQUET=ON \ + -DARROW_S3=ON \ + -DARROW_SUBSTRAIT=ON \ + -DARROW_USE_CCACHE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=java-dist \ + -DCMAKE_UNITY_BUILD=ON + $ cmake --build cpp-jni --target install --config Release + $ cmake \ + -S java \ + -B java-jni \ + -DARROW_JAVA_JNI_ENABLE_C=OFF \ + -DARROW_JAVA_JNI_ENABLE_DEFAULT=ON \ + -DBUILD_TESTING=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=java-dist \ + -DCMAKE_PREFIX_PATH=$PWD/java-dist \ + -DProtobuf_ROOT=$PWD/../cpp-jni/protobuf_ep-install \ + -DProtobuf_USE_STATIC_LIBS=ON + $ cmake --build java-jni --target install --config Release + $ ls -latr java-dist/lib/ + |__ arrow_dataset_jni/ + |__ arrow_orc_jni/ + |__ gandiva_jni/ + +- To build all JNI libraries (Windows) except the JNI C Data Interface library: + + .. code-block:: + + $ cd arrow + $ mkdir java-dist, cpp-jni + $ cmake ^ + -S cpp ^ + -B cpp-jni ^ + -DARROW_BUILD_SHARED=OFF ^ + -DARROW_CSV=ON ^ + -DARROW_DATASET=ON ^ + -DARROW_DEPENDENCY_USE_SHARED=OFF ^ + -DARROW_FILESYSTEM=ON ^ + -DARROW_GANDIVA=OFF ^ + -DARROW_JSON=ON ^ + -DARROW_ORC=ON ^ + -DARROW_PARQUET=ON ^ + -DARROW_S3=ON ^ + -DARROW_SUBSTRAIT=ON ^ + -DARROW_USE_CCACHE=ON ^ + -DARROW_WITH_BROTLI=ON ^ + -DARROW_WITH_LZ4=ON ^ + -DARROW_WITH_SNAPPY=ON ^ + -DARROW_WITH_ZLIB=ON ^ + -DARROW_WITH_ZSTD=ON ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_INSTALL_PREFIX=java-dist ^ + -DCMAKE_UNITY_BUILD=ON ^ + -GNinja + $ cd cpp-jni + $ ninja install + $ cd ../ + $ cmake ^ + -S java ^ + -B java-jni ^ + -DARROW_JAVA_JNI_ENABLE_C=OFF ^ + -DARROW_JAVA_JNI_ENABLE_DATASET=ON ^ + -DARROW_JAVA_JNI_ENABLE_DEFAULT=ON ^ + -DARROW_JAVA_JNI_ENABLE_GANDIVA=OFF ^ + -DARROW_JAVA_JNI_ENABLE_ORC=ON ^ + -DBUILD_TESTING=OFF ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_INSTALL_PREFIX=java-dist ^ + -DCMAKE_PREFIX_PATH=$PWD/java-dist + $ cmake --build java-jni --target install --config Release + $ dir "java-dist/bin" + |__ arrow_orc_jni/ + |__ arrow_dataset_jni/ + +Archery +~~~~~~~ + +.. code-block:: text + + $ cd arrow + $ archery docker run java-jni-manylinux-2014 + $ ls -latr java-dist + |__ arrow_cdata_jni/ + |__ arrow_dataset_jni/ + |__ arrow_orc_jni/ + |__ gandiva_jni/ + +Building Java JNI Modules +------------------------- + +- To compile the JNI bindings, use the ``arrow-c-data`` Maven profile: + + .. code-block:: + + $ cd arrow/java + $ mvn -Darrow.c.jni.dist.dir=/java-dist/lib -Parrow-c-data clean install + +- To compile the JNI bindings for ORC / Gandiva / Dataset, use the ``arrow-jni`` Maven profile: + + .. code-block:: + + $ cd arrow/java + $ mvn \ + -Darrow.cpp.build.dir=/java-dist/lib/ \ + -Darrow.c.jni.dist.dir=/java-dist/lib/ \ + -Parrow-jni clean install + +Testing +======= + +By default, Maven uses the same Java version to both build the code and run the tests. + +It is also possible to use a different JDK version for the tests. This requires Maven +toolchains to be configured beforehand, and then a specific test property needs to be set. + +Configuring Maven toolchains +---------------------------- + +To be able to use a JDK version for testing, it needs to be registered first in Maven ``toolchains.xml`` +configuration file usually located under ``${HOME}/.m2`` with the following snippet added to it: + + .. code-block:: + + + + + [...] + + + jdk + + 21 + temurin + + + path/to/jdk/home + + + + [...] + + + +Testing with a specific JDK +--------------------------- + +To run Arrow tests with a specific JDK version, use the ``arrow.test.jdk-version`` property. + +For example, to run Arrow tests with JDK 17, use the following snippet: + + .. code-block:: + + $ cd arrow/java + $ mvn -Darrow.test.jdk-version=17 clean verify + +IDE Configuration +================= + +IntelliJ +-------- + +To start working on Arrow in IntelliJ: build the project once from the command +line using ``mvn clean install``. Then open the ``java/`` subdirectory of the +Arrow repository, and update the following settings: + +* In the Files tool window, find the path ``vector/target/generated-sources``, + right click the directory, and select Mark Directory as > Generated Sources + Root. There is no need to mark other generated sources directories, as only + the ``vector`` module generates sources. +* For JDK 11, due to an `IntelliJ bug + `__, you must go into + Settings > Build, Execution, Deployment > Compiler > Java Compiler and disable + "Use '--release' option for cross-compilation (Java 9 and later)". Otherwise + you will get an error like "package sun.misc does not exist". +* You may want to disable error-prone entirely if it gives spurious + warnings (disable both error-prone profiles in the Maven tool window + and "Reload All Maven Projects"). +* If using IntelliJ's Maven integration to build, you may need to change + ```` to ``false`` in the pom.xml files due to an `IntelliJ bug + `__. +* To enable debugging JNI-based modules like ``dataset``, + activate specific profiles in the Maven tab under "Profiles". + Ensure the profiles ``arrow-c-data``, ``arrow-jni``, ``generate-libs-cdata-all-os``, + ``generate-libs-jni-macos-linux``, and ``jdk11+`` are enabled, so that the + IDE can build them and enable debugging. + +You may not need to update all of these settings if you build/test with the +IntelliJ Maven integration instead of with IntelliJ directly. + +Common Errors +============= + +* When working with the JNI code: if the C++ build cannot find dependencies, with errors like these: + + .. code-block:: + + Could NOT find Boost (missing: Boost_INCLUDE_DIR system filesystem) + Could NOT find Lz4 (missing: LZ4_LIB) + Could NOT find zstd (missing: ZSTD_LIB) + + Specify that the dependencies should be downloaded at build time (more details at `Dependency Resolution`_): + + .. code-block:: + + -Dre2_SOURCE=BUNDLED \ + -DBoost_SOURCE=BUNDLED \ + -Dutf8proc_SOURCE=BUNDLED \ + -DSnappy_SOURCE=BUNDLED \ + -DORC_SOURCE=BUNDLED \ + -DZLIB_SOURCE=BUNDLED + +.. _Archery: https://github.com/apache/arrow/blob/main/dev/archery/README.md +.. _Dependency Resolution: https://arrow.apache.org/docs/developers/cpp/building.html#individual-dependency-resolution +.. _C++ shared libraries: https://arrow.apache.org/docs/cpp/build_system.html + + +Installing Nightly Packages +=========================== + +.. warning:: + These packages are not official releases. Use them at your own risk. + +Arrow nightly builds are posted on the mailing list at `builds@arrow.apache.org`_. +The artifacts are uploaded to GitHub. For example, for 2022/07/30, they can be found at `GitHub Nightly`_. + + +Installing from Apache Nightlies +-------------------------------- +1. Look up the nightly version number for the Arrow libraries used. + + For example, for ``arrow-memory``, visit https://nightlies.apache.org/arrow/java/org/apache/arrow/arrow-memory/ and see what versions are available (e.g. 9.0.0.dev501). +2. Add Apache Nightlies Repository to the Maven/Gradle project. + + .. code-block:: xml + + + 9.0.0.dev501 + + ... + + + arrow-apache-nightlies + https://nightlies.apache.org/arrow/java + + + ... + + + org.apache.arrow + arrow-vector + ${arrow.version} + + + ... + +Installing Manually +------------------- + +1. Decide nightly packages repository to use, for example: https://github.com/ursacomputing/crossbow/releases/tag/nightly-packaging-2022-07-30-0-github-java-jars +2. Add packages to your pom.xml, for example: flight-core (it depends on: arrow-format, arrow-vector, arrow-memory-core and arrow-memory-netty). + + .. code-block:: xml + + + 8 + 8 + 9.0.0.dev501 + + + + + org.apache.arrow + flight-core + ${arrow.version} + + + +3. Download the necessary pom and jar files to a temporary directory: + + .. code-block:: shell + + $ mkdir nightly-packaging-2022-07-30-0-github-java-jars + $ cd nightly-packaging-2022-07-30-0-github-java-jars + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-java-root-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-format-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-format-9.0.0.dev501.jar + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-vector-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-vector-9.0.0.dev501.jar + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-memory-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-memory-core-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-memory-netty-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-memory-core-9.0.0.dev501.jar + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-memory-netty-9.0.0.dev501.jar + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/arrow-flight-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/flight-core-9.0.0.dev501.pom + $ wget https://github.com/ursacomputing/crossbow/releases/download/nightly-packaging-2022-07-30-0-github-java-jars/flight-core-9.0.0.dev501.jar + $ tree + . + ├── arrow-flight-9.0.0.dev501.pom + ├── arrow-format-9.0.0.dev501.jar + ├── arrow-format-9.0.0.dev501.pom + ├── arrow-java-root-9.0.0.dev501.pom + ├── arrow-memory-9.0.0.dev501.pom + ├── arrow-memory-core-9.0.0.dev501.jar + ├── arrow-memory-core-9.0.0.dev501.pom + ├── arrow-memory-netty-9.0.0.dev501.jar + ├── arrow-memory-netty-9.0.0.dev501.pom + ├── arrow-vector-9.0.0.dev501.jar + ├── arrow-vector-9.0.0.dev501.pom + ├── flight-core-9.0.0.dev501.jar + └── flight-core-9.0.0.dev501.pom + +4. Install the artifacts to the local Maven repository with ``mvn install:install-file``: + + .. code-block:: shell + + $ mvn install:install-file -Dfile="$(pwd)/arrow-java-root-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-java-root -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-format-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-format -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-format-9.0.0.dev501.jar" -DgroupId=org.apache.arrow -DartifactId=arrow-format -Dversion=9.0.0.dev501 -Dpackaging=jar + $ mvn install:install-file -Dfile="$(pwd)/arrow-vector-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-vector -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-vector-9.0.0.dev501.jar" -DgroupId=org.apache.arrow -DartifactId=arrow-vector -Dversion=9.0.0.dev501 -Dpackaging=jar + $ mvn install:install-file -Dfile="$(pwd)/arrow-memory-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-memory -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-memory-core-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-memory-core -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-memory-netty-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-memory-netty -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/arrow-memory-core-9.0.0.dev501.jar" -DgroupId=org.apache.arrow -DartifactId=arrow-memory-core -Dversion=9.0.0.dev501 -Dpackaging=jar + $ mvn install:install-file -Dfile="$(pwd)/arrow-memory-netty-9.0.0.dev501.jar" -DgroupId=org.apache.arrow -DartifactId=arrow-memory-netty -Dversion=9.0.0.dev501 -Dpackaging=jar + $ mvn install:install-file -Dfile="$(pwd)/arrow-flight-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=arrow-flight -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/flight-core-9.0.0.dev501.pom" -DgroupId=org.apache.arrow -DartifactId=flight-core -Dversion=9.0.0.dev501 -Dpackaging=pom + $ mvn install:install-file -Dfile="$(pwd)/flight-core-9.0.0.dev501.jar" -DgroupId=org.apache.arrow -DartifactId=flight-core -Dversion=9.0.0.dev501 -Dpackaging=jar + +5. Validate that the packages were installed: + + .. code-block:: shell + + $ tree ~/.m2/repository/org/apache/arrow + . + ├── arrow-flight + │   ├── 9.0.0.dev501 + │   │   └── arrow-flight-9.0.0.dev501.pom + ├── arrow-format + │   ├── 9.0.0.dev501 + │   │   ├── arrow-format-9.0.0.dev501.jar + │   │   └── arrow-format-9.0.0.dev501.pom + ├── arrow-java-root + │   ├── 9.0.0.dev501 + │   │   └── arrow-java-root-9.0.0.dev501.pom + ├── arrow-memory + │   ├── 9.0.0.dev501 + │   │   └── arrow-memory-9.0.0.dev501.pom + ├── arrow-memory-core + │   ├── 9.0.0.dev501 + │   │   ├── arrow-memory-core-9.0.0.dev501.jar + │   │   └── arrow-memory-core-9.0.0.dev501.pom + ├── arrow-memory-netty + │   ├── 9.0.0.dev501 + │   │   ├── arrow-memory-netty-9.0.0.dev501.jar + │   │   └── arrow-memory-netty-9.0.0.dev501.pom + ├── arrow-vector + │   ├── 9.0.0.dev501 + │   │   ├── _remote.repositories + │   │   ├── arrow-vector-9.0.0.dev501.jar + │   │   └── arrow-vector-9.0.0.dev501.pom + └── flight-core + ├── 9.0.0.dev501 + │   ├── flight-core-9.0.0.dev501.jar + │   └── flight-core-9.0.0.dev501.pom + +6. Compile your project like usual with ``mvn clean install``. + +.. _builds@arrow.apache.org: https://lists.apache.org/list.html?builds@arrow.apache.org +.. _GitHub Nightly: https://github.com/ursacomputing/crossbow/releases/tag/nightly-packaging-2022-07-30-0-github-java-jars + +Installing Staging Packages +=========================== + +.. warning:: + These packages are not official releases. Use them at your own risk. + +Arrow staging builds are created when a Release Candidate (RC) is being prepared. This allows users to test the RC in their applications before voting on the release. + + +Installing from Apache Staging +-------------------------------- +1. Look up the next version number for the Arrow libraries used. + +2. Add Apache Staging Repository to the Maven/Gradle project. + + .. code-block:: xml + + + 9.0.0 + + ... + + + arrow-apache-staging + https://repository.apache.org/content/repositories/staging + + + ... + + + org.apache.arrow + arrow-vector + ${arrow.version} + + + ... diff --git a/docs/source/developers/development.rst b/docs/source/developers/development.rst new file mode 100644 index 00000000..dd183925 --- /dev/null +++ b/docs/source/developers/development.rst @@ -0,0 +1,197 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +.. highlight:: console + +====================== +Development Guidelines +====================== + +.. contents:: + +Logger Abstraction +================== + +Apache Arrow Java uses the SLF4J API, so please configure SLF4J to see logs (e.g. via Logback/Apache Log4j): + +1. If no jar dependencies are added by the user via Logback or Apache Log4j then SLF4J will default + to no-operation (NOP) logging. + +2. If a user adds any dependencies via Logback or Apache Log4j but does not configure/add/define + logback.xml/log4j2.xml, then logs will default to DEBUG mode. + +3. To disable debug logs, the user must define their own rules within their logback.xml/log4j2.xml + and define their own loggers. + +Unit Testing +============ +Unit tests are run by Maven during the build. + +To speed up the build, you can skip them by passing -DskipTests. + +.. code-block:: + + $ cd arrow/java + $ mvn \ + -Darrow.cpp.build.dir=../java-dist/lib -Parrow-jni \ + -Darrow.c.jni.dist.dir=../java-dist/lib -Parrow-c-data \ + clean install + +Performance Testing +=================== + +The ``arrow-performance`` module contains benchmarks. + +Let's configure our environment to run performance tests: + +- Install `benchmark`_ +- Install `archery`_ + +In case you need to see your performance tests on the UI, then, configure (optional): + +- Install `conbench`_ + +Lets execute benchmark tests: + +.. code-block:: + + $ cd benchmarks + $ conbench java-micro --help + $ conbench java-micro + --iterations=1 + --commit=e90472e35b40f58b17d408438bb8de1641bfe6ef + --java-home= + --src= + --benchmark-filter=org.apache.arrow.adapter.AvroAdapterBenchmarks.testAvroToArrow + Benchmark Mode Cnt Score Error Units + AvroAdapterBenchmarks.testAvroToArrow avgt 725545.783 ns/op + Time to POST http://localhost:5000/api/login/ 0.14911699295043945 + Time to POST http://localhost:5000/api/benchmarks/ 0.06116318702697754 + +Then go to: http://127.0.0.1:5000/ to see reports: + +UI Home: + +.. image:: img/conbench_ui.png + +UI Runs: + +.. image:: img/conbench_runs.png + +UI Benchmark: + +.. image:: img/conbench_benchmark.png + +Integration Testing +=================== + +Integration tests can be run :ref:`via Archery `. +For example, assuming you only built Arrow Java and want to run the IPC +integration tests, you would do: + +.. code-block:: console + + $ archery integration --run-ipc --with-java 1 + +Code Style +========== + +The current Java code follows the `Google Java Style`_ with Apache license headers. + +Java code style is checked by `Spotless`_ during the build, and the continuous integration build will verify +that changes adhere to the style guide. + +Automatically fixing code style issues +-------------------------------------- + +- You can check the style without building the project with ``mvn spotless:check``. +- You can autoformat the source with ``mvn spotless:apply``. + +Example: + +.. code-block:: bash + + The following files had format violations: + src/main/java/org/apache/arrow/algorithm/rank/VectorRank.java + @@ -15,7 +15,6 @@ + ·*·limitations·under·the·License. + ·*/ + + - + package·org.apache.arrow.algorithm.rank; + + import·java.util.stream.IntStream; + Run 'mvn spotless:apply' to fix these violations. + +Code Formatter for Intellij IDEA and Eclipse +-------------------------------------------- + +Follow the instructions to set up google-java-format for: + +- `Eclipse`_ +- `IntelliJ`_ + + +Checkstyle +---------- + +Checkstyle is also used for general linting. The configuration is located at `checkstyle`_. +You can also just check the style without building the project. +This checks the code style of all source code under the current directory or from within an individual module. + +.. code-block:: + + $ mvn checkstyle:check + +Maven ``pom.xml`` style is enforced with Spotless using `Apache Maven pom.xml guidelines`_ +You can also just check the style without building the project. +This checks the style of all pom.xml files under the current directory or from within an individual module. + +.. code-block:: + + $ mvn spotless:check + +This applies the style to all pom.xml files under the current directory or from within an individual module. + +.. code-block:: + + $ mvn spotless:apply + +.. _benchmark: https://github.com/ursacomputing/benchmarks +.. _archery: https://github.com/apache/arrow/blob/main/dev/conbench_envs/README.md#L188 +.. _conbench: https://github.com/conbench/conbench +.. _checkstyle: https://github.com/apache/arrow/blob/main/java/dev/checkstyle/checkstyle.xml +.. _Apache Maven pom.xml guidelines: https://maven.apache.org/developers/conventions/code.html#pom-code-convention +.. _Spotless: https://github.com/diffplug/spotless +.. _Google Java Style: https://google.github.io/styleguide/javaguide.html +.. _Eclipse: https://github.com/google/google-java-format?tab=readme-ov-file#eclipse +.. _IntelliJ: https://github.com/google/google-java-format?tab=readme-ov-file#intellij-android-studio-and-other-jetbrains-ides + +Build Caching +============= + +Build caching is done through Develocity (formerly Maven Enterprise). To force +a build without the cache, run:: + + mvn clean install -Ddevelocity.cache.local.enabled=false -Ddevelocity.cache.remote.enabled=false + +This can be useful to make sure you see all warnings from ErrorProne, for example. + +ErrorProne +========== + +ErrorProne should be disabled for generated code. diff --git a/docs/source/developers/img/conbench_benchmark.png b/docs/source/developers/img/conbench_benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..3adf3e8cd4fde3f16a81b4619de5d6937f9a6b2e GIT binary patch literal 141075 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

2lZ#Jif!8FQ!O-OB1OoYXE# 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�XUQyJCLAMvi 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

57>=slan@qCaRf@W<)t#j2es6gH{Lz~cb@U`$Pk znq~c6pf{qXw|*C_6cdbC07pavLPm8%pm$IP$YHB9{w>WXIIp_JbqUKwkL6AO%wBAd z61s?KWxe>yVMfQY$AW8_0t34@93rGR*mqTTcx1X#(7Cj^LDO^_tMLfgV`cl9yFC3s zG<<&FHq8CAs|^qHtIf~VrJ4JBnP_BU;oT$B?yRrD{d6=#>x}yZ`x(ctE$`DQvv0m4gQK^ki7ux)pPDTx>q~!A9i#n1=zGl;6Jx`5gz-d4QtiZt z%F7dushMrslqTuS!XcGr6vFpT(YjXhPB=G*?Kn9xzM*$H9iL`0|?+`O}J9J{C>g&e&rs(U`e zI}_UgQJ@)Ir8|aS8WzPzc-KcRy;{+{msDe+CW=}C;hEpb>Ci7wnvDQuN*Qazbm|VA z@x+&~#{@T0oh}1qo32+;II4rkQ}=iTLv>vGIqeW{Xa>&;&r_K4?6Bj+1mdMv<{f

h*tk8te%)=XM_O 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ê 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/source/developers/img/conbench_ui.png b/docs/source/developers/img/conbench_ui.png new file mode 100644 index 0000000000000000000000000000000000000000..2f72df024b8011a2d947d3208da938804fba3449 GIT binary patch literal 70336 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 z z_wzw`q3=%q;y7`w>5#i4mFvkZ;%30Zs;;{B#^39}qx9z9JDqDs&i%UAWXbfZ!FBQS zzWQ=ycDdzXdG?^?pg2=V+)8vIdFj6SOtNNgb60)KhxMM%8Ed@|-O>HGPAWjM=#*t? zv`3NLs3UQI!=y`TU@dn*St1a*@l0vvOG1u(AbWv^sI-y}Cz~9ng5rdd=CMuO7K^kV7kmR^}^&hGF1^lt7Hv`$hKIkax^ zIgHLZI1HLgWuLtMp{>8_(VcqCO^NH@H+}P%UkJX&_IU={Ko^8=127aF#ZTNnj1!Rl z)H{6PN_5yR{ALeXdmqr)_6+5Tw${Beb~hES2r(kS>Lkpo(@{dl!?FGV!S-594cY|& zfc-9}FgijM)6U_}Ch>0z9qjA~&%db)ywG9@YI<|k1vAx1p_ftt=$vN5kO*LQ ze7xnKa|8n-UlG4g=SP~DF`pKJ+?(ipk;ggjo65=bisR#dXeothKVxQjmXuIW^rOF> zUX`UpfqCZPjSEchQato~S zGZD0YST5Wv^2wK!eZ*yP&+mIl6KAy}D-LliwMTwv=R;l6u3tc)FvE_}ygD^EvVLx8{56uRtP0LO4M9@8exY zV#~9oSL`J?|Mb7w7b{8t_@5R^HUIh5J^DqKhbGQ|yT83Dr(916f>+U5;QyW2{4X1Q zH<}bK`$W?1i!|PuGQpgQ*dlZ#bH#C zO!3_0wbJWnjOvtJtahA9(OExrqPyralDh0N|mT=fk2H=5DZ016$fO)?e~1p+6+35^@T7GpqvsUx5wm0fNR4j(%SzgXW- zWXT;@c$~X`L#lPt#YFa|DLTH;ST{@#oFKhH@UkpFJNB@EOzZpKxMQa(rm-?}IF)Wo zyo#N+T(!?}edp;rcEZ^vNUDUFf%~c5Gu3C5F=(c{DgosHH%q@5bc0iZl{6;mKZpwJAgfOLo{4uTU4|*K0s>8(YTw#diEadU! zS{KWMOjLh(FhlIM`aDP1!F3!(-HBh$o{wM5PSg@tF*8F9t{i=gfiEKb_ISxY2Jhhv zC1xbn5)VC#jxW$PTy^g3ZqvkiaW60~qhY>9_ zIr`<$ZB5_zs&J(Oyo2~u`+7ZJ#K3dsImyG=TO2~Anet%E!CHoWuVrqNEDb=&e{Ml^ zWTgMxRGu46&f7>&t>sWH=LZ?}0lOMW=*8kgP!(>k0SzRLfQ5yyuON^DZ6909)pn zk_V^)jS=~W#rO|cjdB@K0L95tQ0Vx^ib8lVx(-kMglaGDv79f&uhu8$4w4!Uy;SaR zr{*>`P3}|YPs2_0IkO~h&m}jmkRAhG2`7CG37PS|cn)!Bg!|I|-NWU`1DoIN0Nq%% zk2TC?v&dJ=D63ZbKSMguln4OOWpglzjW98G>Hc23ZY`5!VE^Hx;{9=r=*EcO&G*|W zhX?i+e@Oiw*1A8Nsn1DIPsMA{_svMGQIcS-xxW_*@WOpKB=L*2XSgW6dIrnUFodDy zf6z1Zft>+I@c#4SI7dN9<&S)fo$bWvYS}i0n zC+DfM)|i}Lsbg8=_4S-{PdZ62x9=H)^LWdhzWx2i&yBoPM_$5cIJ#|rYTfjOZ1_E# zvTK3r~nW ze*8>%t=nOs6%*U?D(tcI`JP^?eDNoWFx&sFNETG%GNI)R{*=}y=`2MO^xMlKKp)T$ z85uOL#25{HS;77%BZ*<~f2bCk|K$pF&!bqu|Ia1-=l=tD)j;WDh%x$QWjJ~|!T|h2 zBMIF8i*lo<0Qz%N2xQ@*r-SEMyh3wK0LjX<|3kMSh69ei{tw&+P1}){Mn3m%`>HVy zMCTih6By8Wpm{X`9_OtA*xCW37doU$refvM*#CpcvAj4~&_4KsCid)`MpT6UoR``T z-0EqY0j#*5!2kFRlOUYlMg{^`EpRrKGgp0;6YFCL!ce z$-LCZR`Ks>JP;FV8b`wGyy&4EmuN~rig-n`03P`1FX3h0TK6}&Og;|u)r{u&SM+ab z1jC++fu9;drR}OJDyJrsAmqwm3V-52pIWUs5;Z9Bt~Le}xjkR!az8-*;vF#1ac`QJ z1@$7x5-slnafx7b5R~Je(XWi$03g$F=mh8*lSv()iKvbh-B;lcPBkgF5#mo;IA*#?*={u&lhxCX=InvF`Z4*3Ym83xQ$nxz&Q@t9QqG-OSk{FyXlaIQFb(8T9(MGgA^u0p@u z+Vih~m`@r+=<1` + Documentation of the Flight protocol, including how to use + Flight conceptually. + + :external+cookbook:doc:`Java Cookbook ` + Recipes for using Arrow Flight in Java. + +Writing a Flight Service +======================== + +Flight servers implement the `FlightProducer`_ interface. For convenience, +they can subclass `NoOpFlightProducer`_ instead, which offers default +implementations of all the RPC methods. + +.. code-block:: Java + + public class TutorialFlightProducer implements FlightProducer { + @Override + // Override methods or use NoOpFlightProducer for only methods needed + } + +Each RPC method always takes a ``CallContext`` for common parameters. To indicate +failure, pass an exception to the "listener" if present, or else raise an +exception. + +.. code-block:: Java + + // Server + @Override + public void listFlights(CallContext context, Criteria criteria, StreamListener listener) { + // ... + listener.onError( + CallStatus.UNAUTHENTICATED.withDescription( + "Custom UNAUTHENTICATED description message.").toRuntimeException()); + // ... + } + + // Client + try{ + Iterable flightInfosBefore = flightClient.listFlights(Criteria.ALL); + // ... + } catch (FlightRuntimeException e){ + // Catch UNAUTHENTICATED exception + } + +To start a server, create a `Location`_ to specify where to listen, and then create +a `FlightServer`_ with an instance of a producer. This will start the server, but +won't block the rest of the program. Call ``FlightServer.awaitTermination`` +to block until the server stops. + +.. code-block:: Java + + class TutorialFlightProducer implements FlightProducer { + @Override + // Override methods or use NoOpFlightProducer for only methods needed + } + + Location location = Location.forGrpcInsecure("0.0.0.0", 0); + try( + BufferAllocator allocator = new RootAllocator(); + FlightServer server = FlightServer.builder( + allocator, + location, + new TutorialFlightProducer() + ).build(); + ){ + server.start(); + System.out.println("Server listening on port " + server.getPort()); + server.awaitTermination(); + } catch (Exception e) { + e.printStackTrace(); + } + +.. code-block:: shell + + Server listening on port 58104 + +Using the Flight Client +======================= + +To connect to a Flight service, create a `FlightClient`_ with a location. + +.. code-block:: Java + + Location location = Location.forGrpcInsecure("0.0.0.0", 58104); + + try(BufferAllocator allocator = new RootAllocator(); + FlightClient client = FlightClient.builder(allocator, location).build()){ + // ... Consume operations exposed by Flight server + } catch (Exception e) { + e.printStackTrace(); + } + +Cancellation and Timeouts +========================= + +When making a call, clients can optionally provide ``CallOptions``. This allows +clients to set a timeout on calls. Also, some objects returned by client RPC calls +expose a cancel method which allows terminating a call early. + +.. code-block:: Java + + Location location = Location.forGrpcInsecure("0.0.0.0", 58609); + + try(BufferAllocator allocator = new RootAllocator(); + FlightClient tutorialFlightClient = FlightClient.builder(allocator, location).build()){ + + Iterator resultIterator = tutorialFlightClient.doAction( + new Action("test-timeout"), + CallOptions.timeout(2, TimeUnit.SECONDS) + ); + } catch (Exception e) { + e.printStackTrace(); + } + +On the server side, timeouts are transparent. For cancellation, the server needs to manually poll +``setOnCancelHandler`` or ``isCancelled`` to check if the client has cancelled the call, +and if so, break out of any processing the server is currently doing. + +.. code-block:: Java + + // Client + Location location = Location.forGrpcInsecure("0.0.0.0", 58609); + try(BufferAllocator allocator = new RootAllocator(); + FlightClient tutorialFlightClient = FlightClient.builder(allocator, location).build()){ + try(FlightStream flightStream = flightClient.getStream(new Ticket(new byte[]{}))) { + // ... + flightStream.cancel("tutorial-cancel", new Exception("Testing cancellation option!")); + } + } catch (Exception e) { + e.printStackTrace(); + } + // Server + @Override + public void getStream(CallContext context, Ticket ticket, ServerStreamListener listener) { + // ... + listener.setOnCancelHandler(()->{ + // Implement logic to handle cancellation option + }); + } + +Enabling TLS +============ + +TLS can be enabled when setting up a server by providing a +certificate and key pair to ``FlightServer.Builder.useTls``. + +On the client side, use ``Location.forGrpcTls`` to create the Location for the client. + +Enabling Authentication +======================= + +.. warning:: Authentication is insecure without enabling TLS. + +Handshake-based authentication can be enabled by implementing +``ServerAuthHandler``. Authentication consists of two parts: on +initial client connection, the server and client authentication +implementations can perform any negotiation needed. The client authentication +handler then provides a token that will be attached to future calls. + +The client send data to be validated through ``ClientAuthHandler.authenticate`` +The server validate data received through ``ServerAuthHandler.authenticate``. + +Custom Middleware +================= + +Servers and clients support custom middleware (or interceptors) that are called on every +request and can modify the request in a limited fashion. These can be implemented by implementing the +``FlightServerMiddleware`` and ``FlightClientMiddleware`` interfaces. + +Middleware are fairly limited, but they can add headers to a +request/response. On the server, they can inspect incoming headers and +fail the request; hence, they can be used to implement custom +authentication methods. + +Adding Services +=============== + +Servers can add other gRPC services. For example, to add the `Health Check service `_: + +.. code-block:: Java + + final HealthStatusManager statusManager = new HealthStatusManager(); + final Consumer consumer = (builder) -> { + builder.addService(statusManager.getHealthService()); + }; + final Location location = forGrpcInsecure(LOCALHOST, 5555); + try ( + BufferAllocator a = new RootAllocator(Long.MAX_VALUE); + Producer producer = new Producer(a); + FlightServer s = FlightServer.builder(a, location, producer) + .transportHint("grpc.builderConsumer", consumer).build().start(); + ) { + Channel channel = NettyChannelBuilder.forAddress(location.toSocketAddress()).usePlaintext().build(); + HealthCheckResponse response = HealthGrpc + .newBlockingStub(channel) + .check(HealthCheckRequest.getDefaultInstance()); + + System.out.println(response.getStatus()); + } + + +:external+arrow:ref:`Flight best practices ` +=================================================================== + +See the :external+arrow:ref:`best practices for C++ `. + + +.. _`FlightClient`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/FlightClient.html +.. _`FlightProducer`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/FlightProducer.html +.. _`FlightServer`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/FlightServer.html +.. _`NoOpFlightProducer`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/NoOpFlightProducer.html +.. _`Location`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/Location.html diff --git a/docs/source/flight_sql.rst b/docs/source/flight_sql.rst new file mode 100644 index 00000000..169a0e24 --- /dev/null +++ b/docs/source/flight_sql.rst @@ -0,0 +1,32 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +================ +Arrow Flight SQL +================ + +Arrow Flight SQL is an RPC framework for efficient transfer of Arrow data +over the network. + +.. seealso:: + + :external+arrow:doc:`Flight SQL protocol documentation ` + Documentation of the Flight SQL protocol. + +For usage information, see the `API documentation`_. + +.. _API documentation: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/sql/package-summary.html diff --git a/docs/source/flight_sql_jdbc_driver.rst b/docs/source/flight_sql_jdbc_driver.rst new file mode 100644 index 00000000..18069309 --- /dev/null +++ b/docs/source/flight_sql_jdbc_driver.rst @@ -0,0 +1,175 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +============================ +Arrow Flight SQL JDBC Driver +============================ + +The Flight SQL JDBC driver is a JDBC driver implementation that uses +the :external+arrow:doc:`Flight SQL protocol ` under +the hood. This driver can be used with any database that implements +Flight SQL. + +Installation and Requirements +============================= + +The driver is compatible with JDK 11+. Note that the following JVM +parameter is required: + +.. code-block:: shell + + java --add-opens=java.base/java.nio=ALL-UNNAMED ... + +To add a dependency via Maven, use a ``pom.xml`` like the following: + +.. code-block:: xml + + + + 4.0.0 + org.example + demo + 1.0-SNAPSHOT + + 18.1.0 + + + + org.apache.arrow + flight-sql-jdbc-driver + ${arrow.version} + + + + +Connecting to a Database +======================== + +The URI format is as follows:: + + jdbc:arrow-flight-sql://HOSTNAME:PORT[/?param1=val1¶m2=val2&...] + +For example, take this URI:: + + jdbc:arrow-flight-sql://localhost:12345/?username=admin&password=pass&useEncryption=1 + +This will connect to a Flight SQL service running on ``localhost`` on +port 12345. It will create a secure, encrypted connection, and +authenticate using the username ``admin`` and the password ``pass``. + +The components of the URI are as follows. + +* The URI scheme must be ``jdbc:arrow-flight-sql://``. +* **HOSTNAME** is the hostname of the Flight SQL service. +* **PORT** is the port of the Flight SQL service. + +Additional options can be passed as query parameters. Parameter names are +case-sensitive. The supported parameters are: + +.. list-table:: + :header-rows: 1 + + * - Parameter + - Default + - Description + + * - disableCertificateVerification + - false + - When TLS is enabled, whether to verify the server certificate + + * - password + - null + - The password for user/password authentication + + * - threadPoolSize + - 1 + - The size of an internal thread pool + + * - token + - null + - The token used for token authentication + + * - trustStore + - null + - When TLS is enabled, the path to the certificate store + + * - trustStorePassword + - null + - When TLS is enabled, the password for the certificate store + + * - tlsRootCerts + - null + - Path to PEM-encoded root certificates for TLS - use this as + an alternative to ``trustStore`` + + * - clientCertificate + - null + - Path to PEM-encoded client mTLS certificate when the Flight + SQL server requires client verification. + + * - clientKey + - null + - Path to PEM-encoded client mTLS key when the Flight + SQL server requires client verification. + + * - useEncryption + - true + - Whether to use TLS (the default is an encrypted connection) + + * - user + - null + - The username for user/password authentication + + * - useSystemTrustStore + - true + - When TLS is enabled, whether to use the system certificate store + + * - retainCookies + - true + - Whether to use cookies from the initial connection in subsequent + internal connections when retrieving streams from separate endpoints. + + * - retainAuth + - true + - Whether to use bearer tokens obtained from the initial connection + in subsequent internal connections used for retrieving streams + from separate endpoints. + +Note that URI values must be URI-encoded if they contain characters such +as !, @, $, etc. + +Any URI parameters that are not handled by the driver are passed to +the Flight SQL service as gRPC headers. For example, the following URI :: + + jdbc:arrow-flight-sql://localhost:12345/?useEncryption=0&database=mydb + +This will connect without authentication or encryption, to a Flight +SQL service running on ``localhost`` on port 12345. Each request will +also include a ``database=mydb`` gRPC header. + +Connection parameters may also be supplied using the Properties object +when using the JDBC Driver Manager to connect. When supplying using +the Properties object, values should *not* be URI-encoded. + +Parameters specified by the URI supercede parameters supplied by the +Properties object. When calling the `user/password overload of +DriverManager#getConnection() +`_, +the username and password supplied on the URI supercede the username and +password arguments to the function call. diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..5cdf41e1 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,48 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +.. _java: + +Java Implementation +=================== + +This is the documentation of the Java API of Apache Arrow. For more details +on the Arrow format and other language bindings see the :doc:`parent documentation <../index>`. + +.. toctree:: + :maxdepth: 2 + + quickstartguide + overview + install + developers/index + + memory + vector + vector_schema_root + table + ipc + algorithm + flight + flight_sql + flight_sql_jdbc_driver + dataset + substrait + cdata + jdbc + Reference (javadoc) + Cookbook diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 00000000..b2b1c716 --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,230 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +======================= +Installing Java Modules +======================= + +System Compatibility +==================== + +Java modules are regularly built and tested on macOS and Linux distributions. + +Java Compatibility +================== + +Java modules are compatible with JDK 11 and above. Currently, JDK versions +11, 17, 21, and latest are tested in CI. + +Note that some JDK internals must be exposed by +adding ``--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`` to the ``java`` command: + +.. code-block:: shell + + # Directly on the command line + $ java --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -jar ... + # Indirectly via environment variables + $ env JDK_JAVA_OPTIONS="--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" java -jar ... + +Otherwise, you may see errors like ``module java.base does not "opens +java.nio" to unnamed module`` or ``module java.base does not "opens +java.nio" to org.apache.arrow.memory.core`` + +Note that the command has changed from Arrow 15 and earlier. If you are still using the flags from that version +(``--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED``) you will see the +``module java.base does not "opens java.nio" to org.apache.arrow.memory.core`` error. + +If you are using flight-core or dependent modules, you will need to mark that flight-core can read unnamed modules. +Modifying the command above for Flight: + +.. code-block:: shell + + # Directly on the command line + $ java --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -jar ... + # Indirectly via environment variables + $ env JDK_JAVA_OPTIONS="--add-reads=org.apache.arrow.flight.core=ALL-UNNAMED --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" java -jar ... + +Otherwise, you may see errors like ``java.lang.IllegalAccessError: superclass access check failed: class +org.apache.arrow.flight.ArrowMessage$ArrowBufRetainingCompositeByteBuf (in module org.apache.arrow.flight.core) +cannot access class io.netty.buffer.CompositeByteBuf (in unnamed module ...) because module +org.apache.arrow.flight.core does not read unnamed module ...`` + +Finally, if you are using arrow-dataset, you'll also need to report that JDK internals need to be exposed. +Modifying the command above for arrow-memory: + +.. code-block:: shell + + # Directly on the command line + $ java --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -jar ... + # Indirectly via environment variables + $ env JDK_JAVA_OPTIONS="--add-opens=java.base/java.nio=org.apache.arrow.dataset,org.apache.arrow.memory.core,ALL-UNNAMED" java -jar ... + +Otherwise you may see errors such as ``java.lang.RuntimeException: java.lang.reflect.InaccessibleObjectException: +Unable to make static void java.nio.Bits.reserveMemory(long,long) accessible: module +java.base does not "opens java.nio" to module org.apache.arrow.dataset`` + +If using Maven and Surefire for unit testing, :ref:`this argument must +be added to Surefire as well `. + +Installing from Maven +===================== + +By default, Maven will download from the central repository: https://repo.maven.apache.org/maven2/org/apache/arrow/ + +Configure your pom.xml with the Java modules needed, for example: +arrow-vector, and arrow-memory-netty. + +.. code-block:: xml + + + + 4.0.0 + org.example + demo + 1.0-SNAPSHOT + + 9.0.0 + + + + org.apache.arrow + arrow-vector + ${arrow.version} + + + org.apache.arrow + arrow-memory-netty + ${arrow.version} + + + + +A bill of materials (BOM) module has been provided to simplify adding +Arrow modules. This eliminates the need to specify the version for +every module. An alternative to the above would be: + +.. code-block:: xml + + + + 4.0.0 + org.example + demo + 1.0-SNAPSHOT + + 15.0.0 + + + + org.apache.arrow + arrow-vector + + + org.apache.arrow + arrow-memory-netty + + + + + + org.apache.arrow + arrow-bom + ${arrow.version} + pom + import + + + + + +To use the Arrow Flight dependencies, also add the ``os-maven-plugin`` +plugin. This plugin generates useful platform-dependent properties +such as ``os.detected.name`` and ``os.detected.arch`` needed to resolve +transitive dependencies of Flight. + +.. code-block:: xml + + + + 4.0.0 + org.example + demo + 1.0-SNAPSHOT + + 9.0.0 + + + + org.apache.arrow + flight-core + ${arrow.version} + + + + + + kr.motd.maven + os-maven-plugin + 1.7.0 + + + + + +.. _java-install-maven-testing: + +The ``--add-opens`` flag must be added when running unit tests through Maven: + +.. code-block:: xml + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M6 + + --add-opens=java.base/java.nio=ALL-UNNAMED + + + + + +Or they can be added via environment variable, for example when executing your code: + +.. code-block:: + + JDK_JAVA_OPTIONS="--add-opens=java.base/java.nio=ALL-UNNAMED" mvn exec:java -Dexec.mainClass="YourMainCode" + +Installing from Source +====================== + +See :ref:`java-development`. + +IDE Configuration +================= + +Generally, no additional configuration should be needed. However, +ensure your Maven or other build configuration has the ``--add-opens`` +flag as described above, so that the IDE picks it up and runs tests +with that flag as well. diff --git a/docs/source/ipc.rst b/docs/source/ipc.rst new file mode 100644 index 00000000..f5939179 --- /dev/null +++ b/docs/source/ipc.rst @@ -0,0 +1,202 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +=========================== +Reading/Writing IPC formats +=========================== +Arrow defines two types of binary formats for serializing record batches: + +* **Streaming format**: for sending an arbitrary number of record + batches. The format must be processed from start to end, and does not support + random access + +* **File or Random Access format**: for serializing a fixed number of record + batches. It supports random access, and thus is very useful when used with + memory maps + +Writing and Reading Streaming Format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +First, let's populate a :class:`VectorSchemaRoot` with a small batch of records + +.. code-block:: Java + + BitVector bitVector = new BitVector("boolean", allocator); + VarCharVector varCharVector = new VarCharVector("varchar", allocator); + for (int i = 0; i < 10; i++) { + bitVector.setSafe(i, i % 2 == 0 ? 0 : 1); + varCharVector.setSafe(i, ("test" + i).getBytes(StandardCharsets.UTF_8)); + } + bitVector.setValueCount(10); + varCharVector.setValueCount(10); + + List fields = Arrays.asList(bitVector.getField(), varCharVector.getField()); + List vectors = Arrays.asList(bitVector, varCharVector); + VectorSchemaRoot root = new VectorSchemaRoot(fields, vectors); + +Now, we can begin writing a stream containing some number of these batches. For this we use :class:`ArrowStreamWriter` +(DictionaryProvider used for any vectors that are dictionary encoded is optional and can be null)) + +.. code-block:: Java + + try ( + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArrowStreamWriter writer = new ArrowStreamWriter(root, /*DictionaryProvider=*/null, Channels.newChannel(out)); + ) { + // ... do write into the ArrowStreamWriter + } + +Here we used an in-memory stream, but this could have been a socket or some other IO stream. Then we can do + +.. code-block:: Java + + writer.start(); + // write the first batch + writer.writeBatch(); + + // write another four batches. + for (int i = 0; i < 4; i++) { + // populate VectorSchemaRoot data and write the second batch + BitVector childVector1 = (BitVector)root.getVector(0); + VarCharVector childVector2 = (VarCharVector)root.getVector(1); + childVector1.reset(); + childVector2.reset(); + // ... do some populate work here, could be different for each batch + writer.writeBatch(); + } + + writer.end(); + +Note that, since the :class:`VectorSchemaRoot` in the writer is a container that can hold batches, batches flow through +:class:`VectorSchemaRoot` as part of a pipeline, so we need to populate data before ``writeBatch``, so that later batches +could overwrite previous ones. + +Now the :class:`ByteArrayOutputStream` contains the complete stream which contains 5 record batches. +We can read such a stream with :class:`ArrowStreamReader`. Note that the :class:`VectorSchemaRoot` within the reader +will be loaded with new values on every call to :class:`loadNextBatch()` + +.. code-block:: Java + + try (ArrowStreamReader reader = new ArrowStreamReader(new ByteArrayInputStream(out.toByteArray()), allocator)) { + // This will be loaded with new values on every call to loadNextBatch + VectorSchemaRoot readRoot = reader.getVectorSchemaRoot(); + Schema schema = readRoot.getSchema(); + for (int i = 0; i < 5; i++) { + reader.loadNextBatch(); + // ... do something with readRoot + } + } + +Here we also give a simple example with dictionary encoded vectors + +.. code-block:: Java + + // create provider + DictionaryProvider.MapDictionaryProvider provider = new DictionaryProvider.MapDictionaryProvider(); + + try ( + final VarCharVector dictVector = new VarCharVector("dict", allocator); + final VarCharVector vector = new VarCharVector("vector", allocator); + ) { + // create dictionary vector + dictVector.allocateNewSafe(); + dictVector.setSafe(0, "aa".getBytes()); + dictVector.setSafe(1, "bb".getBytes()); + dictVector.setSafe(2, "cc".getBytes()); + dictVector.setValueCount(3); + + // create dictionary + Dictionary dictionary = + new Dictionary(dictVector, new DictionaryEncoding(1L, false, /*indexType=*/null)); + provider.put(dictionary); + + // create original data vector + vector.allocateNewSafe(); + vector.setSafe(0, "bb".getBytes()); + vector.setSafe(1, "bb".getBytes()); + vector.setSafe(2, "cc".getBytes()); + vector.setSafe(3, "aa".getBytes()); + vector.setValueCount(4); + + // get the encoded vector + IntVector encodedVector = (IntVector) DictionaryEncoder.encode(vector, dictionary); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // create VectorSchemaRoot + List fields = Arrays.asList(encodedVector.getField()); + List vectors = Arrays.asList(encodedVector); + try (VectorSchemaRoot root = new VectorSchemaRoot(fields, vectors)) { + + // write data + ArrowStreamWriter writer = new ArrowStreamWriter(root, provider, Channels.newChannel(out)); + writer.start(); + writer.writeBatch(); + writer.end(); + } + + // read data + try (ArrowStreamReader reader = new ArrowStreamReader(new ByteArrayInputStream(out.toByteArray()), allocator)) { + reader.loadNextBatch(); + VectorSchemaRoot readRoot = reader.getVectorSchemaRoot(); + // get the encoded vector + IntVector intVector = (IntVector) readRoot.getVector(0); + + // get dictionaries and decode the vector + Map dictionaryMap = reader.getDictionaryVectors(); + long dictionaryId = intVector.getField().getDictionary().getId(); + try (VarCharVector varCharVector = + (VarCharVector) DictionaryEncoder.decode(intVector, dictionaryMap.get(dictionaryId))) { + // ... use decoded vector + } + } + } + +Writing and Reading Random Access Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The :class:`ArrowFileWriter` has the same API as :class:`ArrowStreamWriter` + +.. code-block:: Java + + try ( + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArrowFileWriter writer = new ArrowFileWriter(root, /*DictionaryProvider=*/null, Channels.newChannel(out)); + ) { + writer.start(); + // write the first batch + writer.writeBatch(); + // write another four batches. + for (int i = 0; i < 4; i++) { + // ... do populate work + writer.writeBatch(); + } + writer.end(); + } + +The difference between :class:`ArrowFileReader` and :class:`ArrowStreamReader` is that the input source +must have a ``seek`` method for random access. Because we have access to the entire payload, we know the +number of record batches in the file, and can read any at random + +.. code-block:: Java + + try (ArrowFileReader reader = new ArrowFileReader( + new ByteArrayReadableSeekableByteChannel(out.toByteArray()), allocator)) { + + // read the 4-th batch + ArrowBlock block = reader.getRecordBlocks().get(3); + reader.loadRecordBatch(block); + VectorSchemaRoot readBatch = reader.getVectorSchemaRoot(); + } diff --git a/docs/source/jdbc.rst b/docs/source/jdbc.rst new file mode 100644 index 00000000..c0477cb0 --- /dev/null +++ b/docs/source/jdbc.rst @@ -0,0 +1,278 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +================== +Arrow JDBC Adapter +================== + +The Arrow JDBC Adapter assists with working with JDBC and Arrow +data. Currently, it supports reading JDBC ResultSets into Arrow +VectorSchemaRoots. + +ResultSet to VectorSchemaRoot Conversion +======================================== + +This can be accessed via the JdbcToArrow class. The resulting +ArrowVectorIterator will convert a ResultSet to Arrow data in batches +of rows. + +.. code-block:: java + + try (ArrowVectorIterator it = JdbcToArrow.sqlToArrowVectorIterator(resultSet, allocator)) { + while (it.hasNext()) { + VectorSchemaRoot root = it.next(); + // Consume the root… + } + } + +The batch size and type mapping can both be customized: + +.. code-block:: java + + JdbcToArrowConfig config = new JdbcToArrowConfigBuilder(allocator, /*calendar=*/null) + .setReuseVectorSchemaRoot(reuseVectorSchemaRoot) + .setJdbcToArrowTypeConverter((jdbcFieldInfo -> { + switch (jdbcFieldInfo.getJdbcType()) { + case Types.BIGINT: + // Assume actual value range is SMALLINT + return new ArrowType.Int(16, true); + default: + return null; + } + })) + .build(); + try (ArrowVectorIterator iter = JdbcToArrow.sqlToArrowVectorIterator(rs, config)) { + while (iter.hasNext()) { + VectorSchemaRoot root = iter.next(); + // Consume the root… + } + } + +The JDBC type can be explicitly specified, which is useful since JDBC +drivers can give spurious type information. For example, the Postgres +driver has been observed to use Decimal types with scale and precision +0; these cases can be handled by specifying the type explicitly before +reading. Also, some JDBC drivers may return BigDecimal values with +inconsistent scale. A RoundingMode can be set to handle these cases: + +.. code-block:: java + + Map mapping = new HashMap<>(); + mapping.put(1, new JdbcFieldInfo(Types.DECIMAL, 20, 7)); + JdbcToArrowConfig config = new JdbcToArrowConfigBuilder(allocator, /*calendar=*/null) + .setBigDecimalRoundingMode(RoundingMode.UNNECESSARY) + .setExplicitTypesByColumnIndex(mapping) + .build(); + try (ArrowVectorIterator iter = JdbcToArrow.sqlToArrowVectorIterator(rs, config)) { + while (iter.hasNext()) { + VectorSchemaRoot root = iter.next(); + // Consume the root… + } + } + +The mapping from JDBC type to Arrow type can be overridden via the +``JdbcToArrowConfig``, but it is not possible to customize the +conversion from JDBC value to Arrow value itself, nor is it possible +to define a conversion for an unsupported type. + +Type Mapping +------------ + +The JDBC to Arrow type mapping can be obtained at runtime from +`JdbcToArrowUtils.getArrowTypeFromJdbcType`_. + +.. _JdbcToArrowUtils.getArrowTypeFromJdbcType: https://arrow.apache.org/docs/java/reference/org/apache/arrow/adapter/jdbc/JdbcToArrowUtils.html#getArrowTypeFromJdbcType-org.apache.arrow.adapter.jdbc.JdbcFieldInfo-java.util.Calendar- + ++--------------------+--------------------+-------+ +| JDBC Type | Arrow Type | Notes | ++====================+====================+=======+ +| ARRAY | List | \(1) | ++--------------------+--------------------+-------+ +| BIGINT | Int64 | | ++--------------------+--------------------+-------+ +| BINARY | Binary | | ++--------------------+--------------------+-------+ +| BIT | Bool | | ++--------------------+--------------------+-------+ +| BLOB | Binary | | ++--------------------+--------------------+-------+ +| BOOLEAN | Bool | | ++--------------------+--------------------+-------+ +| CHAR | Utf8 | | ++--------------------+--------------------+-------+ +| CLOB | Utf8 | | ++--------------------+--------------------+-------+ +| DATE | Date32 | | ++--------------------+--------------------+-------+ +| DECIMAL | Decimal128 | \(2) | ++--------------------+--------------------+-------+ +| DOUBLE | Double | | ++--------------------+--------------------+-------+ +| FLOAT | Float32 | | ++--------------------+--------------------+-------+ +| INTEGER | Int32 | | ++--------------------+--------------------+-------+ +| LONGVARBINARY | Binary | | ++--------------------+--------------------+-------+ +| LONGNVARCHAR | Utf8 | | ++--------------------+--------------------+-------+ +| LONGVARCHAR | Utf8 | | ++--------------------+--------------------+-------+ +| NCHAR | Utf8 | | ++--------------------+--------------------+-------+ +| NULL | Null | | ++--------------------+--------------------+-------+ +| NUMERIC | Decimal128 | | ++--------------------+--------------------+-------+ +| NVARCHAR | Utf8 | | ++--------------------+--------------------+-------+ +| REAL | Float32 | | ++--------------------+--------------------+-------+ +| SMALLINT | Int16 | | ++--------------------+--------------------+-------+ +| STRUCT | Struct | \(3) | ++--------------------+--------------------+-------+ +| TIME | Time32[ms] | | ++--------------------+--------------------+-------+ +| TIMESTAMP | Timestamp[ms] | \(4) | ++--------------------+--------------------+-------+ +| TINYINT | Int8 | | ++--------------------+--------------------+-------+ +| VARBINARY | Binary | | ++--------------------+--------------------+-------+ +| VARCHAR | Utf8 | | ++--------------------+--------------------+-------+ + +* \(1) The list value type must be explicitly configured and cannot be + inferred. Use `setArraySubTypeByColumnIndexMap`_ or + `setArraySubTypeByColumnNameMap`_. +* \(2) By default, the scale of decimal values must match the scale in + the type exactly; precision is allowed to be any value greater or + equal to the type precision. If there is a mismatch, by default, an + exception will be thrown. This can be configured by setting a + different RoundingMode with setBigDecimalRoundingMode. +* \(3) Not fully supported: while the type conversion is defined, the + value conversion is not. See ARROW-17006_. +* \(4) If a Calendar is provided, then the timestamp will have the + timezone of the calendar, else it will be a timestamp without + timezone. + +.. _setArraySubTypeByColumnIndexMap: https://arrow.apache.org/docs/java/reference/org/apache/arrow/adapter/jdbc/JdbcToArrowConfigBuilder.html#setArraySubTypeByColumnIndexMap-java.util.Map- +.. _setArraySubTypeByColumnNameMap: https://arrow.apache.org/docs/java/reference/org/apache/arrow/adapter/jdbc/JdbcToArrowConfigBuilder.html#setArraySubTypeByColumnNameMap-java.util.Map- +.. _ARROW-17006: https://issues.apache.org/jira/browse/ARROW-17006 + +VectorSchemaRoot to PreparedStatement Parameter Conversion +========================================================== + +The adapter can bind rows of Arrow data from a VectorSchemaRoot to +parameters of a JDBC PreparedStatement. This can be accessed via the +JdbcParameterBinder class. Each call to next() will bind parameters +from the next row of data, and then the application can execute the +statement, call addBatch(), etc. as desired. Null values will lead to +a setNull call with an appropriate JDBC type code (listed below). + +.. code-block:: java + + final JdbcParameterBinder binder = + JdbcParameterBinder.builder(statement, root).bindAll().build(); + while (binder.next()) { + statement.executeUpdate(); + } + // Use a VectorLoader to update the root + binder.reset(); + while (binder.next()) { + statement.executeUpdate(); + } + +The mapping of vectors to parameters, the JDBC type code used by the +converters, and the type conversions themselves can all be customized: + +.. code-block:: java + + final JdbcParameterBinder binder = + JdbcParameterBinder.builder(statement, root) + .bind(/*parameterIndex*/2, /*columnIndex*/0) + .bind(/*parameterIndex*/1, customColumnBinderInstance) + .build(); + +Type Mapping +------------ + +The Arrow to JDBC type mapping can be obtained at runtime via +a method on ColumnBinder. + ++----------------------------+----------------------------+-------+ +| Arrow Type | JDBC Type | Notes | ++============================+============================+=======+ +| Binary | VARBINARY (setBytes) | | ++----------------------------+----------------------------+-------+ +| Bool | BOOLEAN (setBoolean) | | ++----------------------------+----------------------------+-------+ +| Date32 | DATE (setDate) | | ++----------------------------+----------------------------+-------+ +| Date64 | DATE (setDate) | | ++----------------------------+----------------------------+-------+ +| Decimal128 | DECIMAL (setBigDecimal) | | ++----------------------------+----------------------------+-------+ +| Decimal256 | DECIMAL (setBigDecimal) | | ++----------------------------+----------------------------+-------+ +| FixedSizeBinary | BINARY (setBytes) | | ++----------------------------+----------------------------+-------+ +| Float32 | REAL (setFloat) | | ++----------------------------+----------------------------+-------+ +| Int8 | TINYINT (setByte) | | ++----------------------------+----------------------------+-------+ +| Int16 | SMALLINT (setShort) | | ++----------------------------+----------------------------+-------+ +| Int32 | INTEGER (setInt) | | ++----------------------------+----------------------------+-------+ +| Int64 | BIGINT (setLong) | | ++----------------------------+----------------------------+-------+ +| LargeBinary | LONGVARBINARY (setBytes) | | ++----------------------------+----------------------------+-------+ +| LargeUtf8 | LONGVARCHAR (setString) | \(1) | ++----------------------------+----------------------------+-------+ +| Time[s] | TIME (setTime) | | ++----------------------------+----------------------------+-------+ +| Time[ms] | TIME (setTime) | | ++----------------------------+----------------------------+-------+ +| Time[us] | TIME (setTime) | | ++----------------------------+----------------------------+-------+ +| Time[ns] | TIME (setTime) | | ++----------------------------+----------------------------+-------+ +| Timestamp[s] | TIMESTAMP (setTimestamp) | \(2) | ++----------------------------+----------------------------+-------+ +| Timestamp[ms] | TIMESTAMP (setTimestamp) | \(2) | ++----------------------------+----------------------------+-------+ +| Timestamp[us] | TIMESTAMP (setTimestamp) | \(2) | ++----------------------------+----------------------------+-------+ +| Timestamp[ns] | TIMESTAMP (setTimestamp) | \(2) | ++----------------------------+----------------------------+-------+ +| Utf8 | VARCHAR (setString) | | ++----------------------------+----------------------------+-------+ + +* \(1) Strings longer than Integer.MAX_VALUE bytes (the maximum length + of a Java ``byte[]``) will cause a runtime exception. +* \(2) If the timestamp has a timezone, the JDBC type defaults to + TIMESTAMP_WITH_TIMEZONE. If the timestamp has no timezone, + technically there is not a correct conversion from Arrow value to + JDBC value, because a JDBC Timestamp is in UTC, and we have no + timezone information. In this case, the default binder will call + `setTimestamp(int, Timestamp) + `_, + which will lead to the driver using the "default timezone" (that of + the Java VM). diff --git a/docs/source/memory.rst b/docs/source/memory.rst new file mode 100644 index 00000000..58ef382d --- /dev/null +++ b/docs/source/memory.rst @@ -0,0 +1,499 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +================= +Memory Management +================= + +The memory modules contain all the functionality that Arrow uses to allocate and deallocate memory. This document is divided in two parts: +The first part, *Memory Basics*, provides a high-level introduction. The following section, *Arrow Memory In-Depth*, fills in the details. + +Memory Basics +============= +This section will introduce you to the major concepts in Java’s memory management: + +* `ArrowBuf`_ +* `BufferAllocator`_ +* Reference counting + +It also provides some guidelines for working with memory in Arrow, and describes how to debug memory issues when they arise. + +Getting Started +--------------- + +Arrow's memory management is built around the needs of the columnar format and using off-heap memory. +Arrow Java has its own independent implementation. It does not wrap the C++ implementation, although the framework is flexible enough +to be used with memory allocated in C++ that is used by Java code. + +Arrow provides multiple modules: the core interfaces, and implementations of the interfaces. +Users need the core interfaces, and exactly one of the implementations. + +* ``memory-core``: Provides the interfaces used by the Arrow libraries and applications. +* ``memory-netty``: An implementation of the memory interfaces based on the `Netty`_ library. +* ``memory-unsafe``: An implementation of the memory interfaces based on the `sun.misc.Unsafe`_ library. + + +ArrowBuf +-------- + +ArrowBuf represents a single, contiguous region of `direct memory`_. It consists of an address and a length, +and provides low-level interfaces for working with the contents, similar to ByteBuffer. + +Unlike (Direct)ByteBuffer, it has reference counting built in, as discussed later. + +Why Arrow Uses Direct Memory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* The JVM can optimize I/O operations when using direct memory/direct buffers; it will attempt to avoid copying buffer contents to/from an intermediate buffer. This can speed up IPC in Arrow. +* Since Arrow always uses direct memory, JNI modules can directly wrap native memory addresses instead of copying data. We use this in modules like the C Data Interface. +* Conversely, on the C++ side of the JNI boundary, we can directly access the memory in ArrowBuf without copying data. + +BufferAllocator +--------------- + +The `BufferAllocator`_ is primarily an arena or nursery used for accounting of buffers (ArrowBuf instances). +As the name suggests, it can allocate new buffers associated with itself, but it can also +handle the accounting for buffers allocated elsewhere. For example, it handles the Java-side accounting for +memory allocated in C++ and shared with Java using the C-Data Interface. In the code below it performs an allocation: + +.. code-block:: Java + + import org.apache.arrow.memory.ArrowBuf; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + + try(BufferAllocator bufferAllocator = new RootAllocator(8 * 1024)){ + ArrowBuf arrowBuf = bufferAllocator.buffer(4 * 1024); + System.out.println(arrowBuf); + arrowBuf.close(); + } + +.. code-block:: shell + + ArrowBuf[2], address:140363641651200, length:4096 + +The concrete implementation of the BufferAllocator interface is `RootAllocator`_. Applications should generally create +one RootAllocator at the start of the program, and use it through the BufferAllocator interface. Allocators implement +AutoCloseable and must be closed after the application is done with them; this will check that all outstanding memory +has been freed (see the next section). + +Arrow provides a tree-based model for memory allocation. The RootAllocator is created first, then more allocators +are created as children of an existing allocator via `newChildAllocator`_. When creating a RootAllocator or a child +allocator, a memory limit is provided, and when allocating memory, the limit is checked. Furthermore, when allocating +memory from a child allocator, those allocations are also reflected in all parent allocators. Hence, the RootAllocator +effectively sets the program-wide memory limit, and serves as the master bookkeeper for all memory allocations. + +Child allocators are not strictly required, but can help better organize code. For instance, a lower memory limit can +be set for a particular section of code. The child allocator can be closed when that section completes, +at which point it checks that that section didn't leak any memory. +Child allocators can also be named, which makes it easier to tell where an ArrowBuf came from during debugging. + +Reference counting +------------------ + +Because direct memory is expensive to allocate and deallocate, allocators may share direct buffers. To manage shared buffers +deterministically, we use manual reference counting instead of the garbage collector. +This simply means that each buffer has a counter keeping track of the number of references to +the buffer, and the user is responsible for properly incrementing/decrementing the counter as the buffer is used. + +In Arrow, each ArrowBuf has an associated `ReferenceManager`_ that tracks the reference count. You can retrieve +it with ArrowBuf.getReferenceManager(). The reference count is updated using `ReferenceManager.release`_ to decrement the count, +and `ReferenceManager.retain`_ to increment it. + +Of course, this is tedious and error-prone, so instead of directly working with buffers, we typically use +higher-level APIs like ValueVector. Such classes generally implement Closeable/AutoCloseable and will automatically +decrement the reference count when closed. + +Allocators implement AutoCloseable as well. In this case, closing the allocator will check that all buffers +obtained from the allocator are closed. If not, ``close()`` method will raise an exception; this helps track +memory leaks from unclosed buffers. + +Reference counting needs to be handled carefully. To ensure that an +independent section of code has fully cleaned up all allocated buffers, use a new child allocator. + +Development Guidelines +---------------------- + +Applications should generally: + +* Use the BufferAllocator interface in APIs instead of RootAllocator. +* Create one RootAllocator at the start of the program and explicitly pass it when needed. +* ``close()`` allocators after use (whether they are child allocators or the RootAllocator), either manually or preferably via a try-with-resources statement. + + +Debugging Memory Leaks/Allocation +--------------------------------- + +In ``DEBUG`` mode, the allocator and supporting classes will record additional +debug tracking information to better track down memory leaks and issues. To +enable DEBUG mode pass the following system property to the VM when starting +``-Darrow.memory.debug.allocator=true``. + +When DEBUG is enabled, a log will be kept of allocations. Configure SLF4J to see these logs (e.g. via Logback/Apache Log4j). +Consider the following example to see how it helps us with the tracking of allocators: + +.. code-block:: Java + + import org.apache.arrow.memory.ArrowBuf; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + + try (BufferAllocator bufferAllocator = new RootAllocator(8 * 1024)) { + ArrowBuf arrowBuf = bufferAllocator.buffer(4 * 1024); + System.out.println(arrowBuf); + } + +Without the debug mode enabled, when we close the allocator, we get this: + +.. code-block:: shell + + 11:56:48.944 [main] INFO o.apache.arrow.memory.BaseAllocator - Debug mode disabled. + ArrowBuf[2], address:140508391276544, length:4096 + 16:28:08.847 [main] ERROR o.apache.arrow.memory.BaseAllocator - Memory was leaked by query. Memory leaked: (4096) + Allocator(ROOT) 0/4096/4096/8192 (res/actual/peak/limit) + +Enabling the debug mode, we get more details: + +.. code-block:: shell + + 11:56:48.944 [main] INFO o.apache.arrow.memory.BaseAllocator - Debug mode enabled. + ArrowBuf[2], address:140437894463488, length:4096 + Exception in thread "main" java.lang.IllegalStateException: Allocator[ROOT] closed with outstanding buffers allocated (1). + Allocator(ROOT) 0/4096/4096/8192 (res/actual/peak/limit) + child allocators: 0 + ledgers: 1 + ledger[1] allocator: ROOT), isOwning: , size: , references: 1, life: 261438177096661..0, allocatorManager: [, life: ] holds 1 buffers. + ArrowBuf[2], address:140437894463488, length:4096 + reservations: 0 + +Additionally, in debug mode, `ArrowBuf.print()`_ can be used to obtain a debug string. +This will include information about allocation operations on the buffer with stack traces, such as when/where the buffer was allocated. + +.. code-block:: java + + import org.apache.arrow.memory.ArrowBuf; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + + try (final BufferAllocator allocator = new RootAllocator()) { + try (final ArrowBuf buf = allocator.buffer(1024)) { + final StringBuilder sb = new StringBuilder(); + buf.print(sb, /*indent*/ 0); + System.out.println(sb.toString()); + } + } + +.. code-block:: text + + ArrowBuf[2], address:140433199984656, length:1024 + event log for: ArrowBuf[2] + 675959093395667 create() + at org.apache.arrow.memory.util.HistoricalLog$Event.(HistoricalLog.java:175) + at org.apache.arrow.memory.util.HistoricalLog.recordEvent(HistoricalLog.java:83) + at org.apache.arrow.memory.ArrowBuf.(ArrowBuf.java:96) + at org.apache.arrow.memory.BufferLedger.newArrowBuf(BufferLedger.java:271) + at org.apache.arrow.memory.BaseAllocator.bufferWithoutReservation(BaseAllocator.java:300) + at org.apache.arrow.memory.BaseAllocator.buffer(BaseAllocator.java:276) + at org.apache.arrow.memory.RootAllocator.buffer(RootAllocator.java:29) + at org.apache.arrow.memory.BaseAllocator.buffer(BaseAllocator.java:240) + at org.apache.arrow.memory.RootAllocator.buffer(RootAllocator.java:29) + at REPL.$JShell$14.do_it$($JShell$14.java:10) + at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2) + at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:566) + at jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209) + at jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116) + at jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119) + at jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:144) + at jdk.jshell.execution.ExecutionControlForwarder.commandLoop(ExecutionControlForwarder.java:262) + at jdk.jshell.execution.Util.forwardExecutionControl(Util.java:76) + at jdk.jshell.execution.Util.forwardExecutionControlAndIO(Util.java:137) + at jdk.jshell.execution.RemoteExecutionControl.main(RemoteExecutionControl.java:70) + +The BufferAllocator also provides a ``BufferAllocator.toVerboseString()`` which can be used in +``DEBUG`` mode to get extensive stacktrace information and events associated with various Allocator behaviors. + +Finally, enabling the ``TRACE`` logging level will automatically provide this stack trace when the allocator is closed: + +.. code-block:: java + + // Assumes use of Logback; adjust for Log4j, etc. as appropriate + import ch.qos.logback.classic.Level; + import ch.qos.logback.classic.Logger; + import org.apache.arrow.memory.ArrowBuf; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.slf4j.LoggerFactory; + + // Set log level to TRACE to get tracebacks + ((Logger) LoggerFactory.getLogger("org.apache.arrow")).setLevel(Level.TRACE); + try (final BufferAllocator allocator = new RootAllocator()) { + // Leak buffer + allocator.buffer(1024); + } + +.. code-block:: text + + | Exception java.lang.IllegalStateException: Allocator[ROOT] closed with outstanding buffers allocated (1). + Allocator(ROOT) 0/1024/1024/9223372036854775807 (res/actual/peak/limit) + child allocators: 0 + ledgers: 1 + ledger[1] allocator: ROOT), isOwning: , size: , references: 1, life: 712040870231544..0, allocatorManager: [, life: ] holds 1 buffers. + ArrowBuf[2], address:139926571810832, length:1024 + event log for: ArrowBuf[2] + 712040888650134 create() + at org.apache.arrow.memory.util.StackTrace.(StackTrace.java:34) + at org.apache.arrow.memory.util.HistoricalLog$Event.(HistoricalLog.java:175) + at org.apache.arrow.memory.util.HistoricalLog.recordEvent(HistoricalLog.java:83) + at org.apache.arrow.memory.ArrowBuf.(ArrowBuf.java:96) + at org.apache.arrow.memory.BufferLedger.newArrowBuf(BufferLedger.java:271) + at org.apache.arrow.memory.BaseAllocator.bufferWithoutReservation(BaseAllocator.java:300) + at org.apache.arrow.memory.BaseAllocator.buffer(BaseAllocator.java:276) + at org.apache.arrow.memory.RootAllocator.buffer(RootAllocator.java:29) + at org.apache.arrow.memory.BaseAllocator.buffer(BaseAllocator.java:240) + at org.apache.arrow.memory.RootAllocator.buffer(RootAllocator.java:29) + at REPL.$JShell$18.do_it$($JShell$18.java:13) + at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2) + at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:566) + at jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209) + at jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116) + at jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119) + at jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:144) + at jdk.jshell.execution.ExecutionControlForwarder.commandLoop(ExecutionControlForwarder.java:262) + at jdk.jshell.execution.Util.forwardExecutionControl(Util.java:76) + at jdk.jshell.execution.Util.forwardExecutionControlAndIO(Util.java:137) + + reservations: 0 + + | at BaseAllocator.close (BaseAllocator.java:405) + | at RootAllocator.close (RootAllocator.java:29) + | at (#8:1) + +Sometimes, explicitly passing allocators around is difficult. For example, it +can be hard to pass around extra state, like an allocator, through layers of +existing application or framework code. A global or singleton allocator instance +can be useful here, though it should not be your first choice. + +How this works: + +1. Set up a global allocator in a singleton class. +2. Provide methods to create child allocators from the global allocator. +3. Give child allocators proper names to make it easier to figure out where + allocations occurred in case of errors. +4. Ensure that resources are properly closed. +5. Check that the global allocator is empty at some suitable point, such as + right before program shutdown. +6. If it is not empty, review the above allocation bugs. + +.. code-block:: java + + //1 + private static final BufferAllocator allocator = new RootAllocator(); + private static final AtomicInteger childNumber = new AtomicInteger(0); + ... + //2 + public static BufferAllocator getChildAllocator() { + return allocator.newChildAllocator(nextChildName(), 0, Long.MAX_VALUE); + } + ... + //3 + private static String nextChildName() { + return "Allocator-Child-" + childNumber.incrementAndGet(); + } + ... + //4: Business code + try (BufferAllocator allocator = GlobalAllocator.getChildAllocator()) { + ... + } + ... + //5 + public static void checkGlobalCleanUpResources() { + ... + if (!allocator.getChildAllocators().isEmpty()) { + throw new IllegalStateException(...); + } else if (allocator.getAllocatedMemory() != 0) { + throw new IllegalStateException(...); + } + } + +.. _`ArrowBuf`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ArrowBuf.html +.. _`ArrowBuf.print()`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ArrowBuf.html#print-java.lang.StringBuilder-int-org.apache.arrow.memory.BaseAllocator.Verbosity- +.. _`BufferAllocator`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/BufferAllocator.html +.. _`BufferLedger`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/BufferLedger.html +.. _`RootAllocator`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/RootAllocator.html +.. _`newChildAllocator`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/RootAllocator.html#newChildAllocator-java.lang.String-org.apache.arrow.memory.AllocationListener-long-long- +.. _`Netty`: https://netty.io/wiki/ +.. _`sun.misc.unsafe`: https://web.archive.org/web/20210929024401/http://www.docjar.com/html/api/sun/misc/Unsafe.java.html +.. _`Direct Memory`: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html +.. _`ReferenceManager`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ReferenceManager.html +.. _`ReferenceManager.release`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ReferenceManager.html#release-- +.. _`ReferenceManager.retain`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ReferenceManager.html#retain-- + +Arrow Memory In-Depth +===================== + +Design Principles +----------------- +Arrow’s memory model is based on the following basic concepts: + +- Memory can be allocated up to some limit. That limit could be a real + limit (OS/JVM) or a locally imposed limit. +- Allocation operates in two phases: accounting then actual allocation. + Allocation could fail at either point. +- Allocation failure should be recoverable. In all cases, the Allocator + infrastructure should expose memory allocation failures (OS or + internal limit-based) as ``OutOfMemoryException``\ s. +- Any allocator can reserve memory when created. This memory shall be + held such that this allocator will always be able to allocate that + amount of memory. +- A particular application component should work to use a local + allocator to understand local memory usage and better debug memory + leaks. +- The same physical memory can be shared by multiple allocators and the + allocator must provide an accounting paradigm for this purpose. + +Reserving Memory +---------------- + +Arrow provides two different ways to reserve memory: + +- BufferAllocator accounting reservations: When a new allocator (other + than the ``RootAllocator``) is initialized, it can set aside memory + that it will keep locally for its lifetime. This is memory that will + never be released back to its parent allocator until the allocator is + closed. +- ``AllocationReservation`` via BufferAllocator.newReservation(): + Allows a short-term preallocation strategy so that a particular + subsystem can ensure future memory is available to support a + particular request. + +Reference Counting Details +-------------------------- + +Typically, the ReferenceManager implementation used is an instance of `BufferLedger`_. +A BufferLedger is a ReferenceManager that also maintains the relationship between an ``AllocationManager``, +a ``BufferAllocator`` and one or more individual ``ArrowBuf``\ s + +All ArrowBufs (direct or sliced) related to a single BufferLedger/BufferAllocator combination +share the same reference count and either all will be valid or all will be invalid. +For simplicity of accounting, we treat that memory as being used by one +of the BufferAllocators associated with the memory. When that allocator +releases its claim on that memory, the memory ownership is then moved to +another BufferLedger belonging to the same AllocationManager. + +Allocation Details +------------------ + +There are several Allocator types in Arrow Java: + +- ``BufferAllocator`` - The public interface application users should be leveraging +- ``BaseAllocator`` - The base implementation of memory allocation, contains the meat of the Arrow allocator implementation +- ``RootAllocator`` - The root allocator. Typically only one created for a JVM. It serves as the parent/ancestor for child allocators +- ``ChildAllocator`` - A child allocator that derives from the root allocator + +Many BufferAllocators can reference the same piece of physical memory at the same +time. It is the AllocationManager’s responsibility to ensure that in this situation, +all memory is accurately accounted for from the Root’s perspective +and also to ensure that the memory is correctly released once all +BufferAllocators have stopped using that memory. + +For simplicity of accounting, we treat that memory as being used by one +of the BufferAllocators associated with the memory. When that allocator +releases its claim on that memory, the memory ownership is then moved to +another BufferLedger belonging to the same AllocationManager. Note that +because a ArrowBuf.release() is what actually causes memory ownership +transfer to occur, we always proceed with ownership transfer (even if +that violates an allocator limit). It is the responsibility of the +application owning a particular allocator to frequently confirm whether +the allocator is over its memory limit (BufferAllocator.isOverLimit()) +and if so, attempt to aggressively release memory to ameliorate the +situation. + + +Object Hierarchy +---------------- + +There are two main ways that someone can look at the object hierarchy +for Arrow’s memory management scheme. The first is a memory based +perspective as below: + +Memory Perspective +~~~~~~~~~~~~~~~~~~ + +.. code-block:: none + + + AllocationManager + | + |-- UnsignedDirectLittleEndian (One per AllocationManager) + | + |-+ BufferLedger 1 ==> Allocator A (owning) + | ` - ArrowBuf 1 + |-+ BufferLedger 2 ==> Allocator B (non-owning) + | ` - ArrowBuf 2 + |-+ BufferLedger 3 ==> Allocator C (non-owning) + | - ArrowBuf 3 + | - ArrowBuf 4 + ` - ArrowBuf 5 + +In this picture, a piece of memory is owned by an allocator manager. An +allocator manager is responsible for that piece of memory no matter +which allocator(s) it is working with. An allocator manager will have +relationships with a piece of raw memory (via its reference to +UnsignedDirectLittleEndian) as well as references to each +BufferAllocator it has a relationship to. + +Allocator Perspective +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: none + + + RootAllocator + |-+ ChildAllocator 1 + | | - ChildAllocator 1.1 + | ` ... + | + |-+ ChildAllocator 2 + |-+ ChildAllocator 3 + | | + | |-+ BufferLedger 1 ==> AllocationManager 1 (owning) ==> UDLE + | | `- ArrowBuf 1 + | `-+ BufferLedger 2 ==> AllocationManager 2 (non-owning)==> UDLE + | `- ArrowBuf 2 + | + |-+ BufferLedger 3 ==> AllocationManager 1 (non-owning)==> UDLE + | ` - ArrowBuf 3 + |-+ BufferLedger 4 ==> AllocationManager 2 (owning) ==> UDLE + | - ArrowBuf 4 + | - ArrowBuf 5 + ` - ArrowBuf 6 + +In this picture, a RootAllocator owns three ChildAllocators. The first +ChildAllocator (ChildAllocator 1) owns a subsequent ChildAllocator. +ChildAllocator has two BufferLedgers/AllocationManager references. +Coincidentally, each of these AllocationManager’s is also associated +with the RootAllocator. In this case, one of the these +AllocationManagers is owned by ChildAllocator 3 (AllocationManager 1) +while the other AllocationManager (AllocationManager 2) is +owned/accounted for by the RootAllocator. Note that in this scenario, +ArrowBuf 1 is sharing the underlying memory as ArrowBuf 3. However the +subset of that memory (e.g. through slicing) might be different. Also +note that ArrowBuf 2 and ArrowBuf 4, 5 and 6 are also sharing the same +underlying memory. Also note that ArrowBuf 4, 5 and 6 all share the same +reference count and fate. diff --git a/docs/source/overview.rst b/docs/source/overview.rst new file mode 100644 index 00000000..be579c14 --- /dev/null +++ b/docs/source/overview.rst @@ -0,0 +1,90 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +=================== +High-Level Overview +=================== + +The Apache Arrow Java modules implement various specifications including the +columnar format and IPC. Most modules are native Java implementations, +but some modules are JNI bindings to the C++ library. + +.. list-table:: Arrow Java Modules + :widths: 25 50 25 + :header-rows: 1 + + * - Module + - Description + - Implementation + * - arrow-format + - Generated Java files from the IPC Flatbuffer definitions. + - Native + * - arrow-memory-core + - Core off-heap memory management libraries for Arrow ValueVectors. + - Native + * - arrow-memory-unsafe + - Memory management implementation based on sun.misc.Unsafe. + - Native + * - arrow-memory-netty + - Memory management implementation based on Netty. + - Native + * - arrow-vector + - An off-heap reference implementation for Arrow columnar data format. + - Native + * - arrow-tools + - Java applications for working with Arrow ValueVectors. + - Native + * - arrow-jdbc + - (Experimental) A library for converting JDBC data to Arrow data. + - Native + * - flight-core + - An RPC mechanism for transferring ValueVectors. + - Native + * - flight-sql + - Contains utility classes to expose Flight SQL semantics for clients and servers over Arrow Flight. + - Native + * - flight-integration-tests + - Integration tests for Flight RPC. + - Native + * - arrow-performance + - JMH benchmarks for the Arrow libraries. + - Native + * - arrow-algorithm + - (Experimental) A collection of algorithms for working with ValueVectors. + - Native + * - arrow-avro + - (Experimental) A library for converting Avro data to Arrow data. + - Native + * - arrow-compression + - (Experimental) A library for working with compression/decompression of Arrow data. + - Native + * - arrow-c-data + - Java implementation of `C Data Interface`_ + - JNI + * - arrow-orc + - (Experimental) A JNI wrapper for the C++ ORC reader implementation. + - JNI + * - arrow-gandiva + - Java wrappers around the native Gandiva SQL expression compiler. + - JNI + * - arrow-dataset + - Java bindings to the Arrow Datasets library. + - JNI + +Arrow Java modules support working with data (1) in-memory, (2) at rest, and (3) on-the-wire. + +.. _`C Data Interface`: https://arrow.apache.org/docs/format/CDataInterface.html diff --git a/docs/source/quickstartguide.rst b/docs/source/quickstartguide.rst new file mode 100644 index 00000000..adb07d70 --- /dev/null +++ b/docs/source/quickstartguide.rst @@ -0,0 +1,314 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +================= +Quick Start Guide +================= + +Arrow Java provides several building blocks. Data types describe the types of values; +ValueVectors are sequences of typed values; fields describe the types of columns in +tabular data; schemas describe a sequence of columns in tabular data, and +VectorSchemaRoot represents tabular data. Arrow also provides readers and +writers for loading data from and persisting data to storage. + +Create a ValueVector +******************** + +**ValueVectors** represent a sequence of values of the same type. +They are also known as "arrays" in the columnar format. + +Example: create a vector of 32-bit integers representing ``[1, null, 2]``: + +.. code-block:: Java + + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.IntVector; + + try( + BufferAllocator allocator = new RootAllocator(); + IntVector intVector = new IntVector("fixed-size-primitive-layout", allocator); + ){ + intVector.allocateNew(3); + intVector.set(0,1); + intVector.setNull(1); + intVector.set(2,2); + intVector.setValueCount(3); + System.out.println("Vector created in memory: " + intVector); + } + +.. code-block:: shell + + Vector created in memory: [1, null, 2] + + +Example: create a vector of UTF-8 encoded strings representing ``["one", "two", "three"]``: + +.. code-block:: Java + + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.VarCharVector; + + try( + BufferAllocator allocator = new RootAllocator(); + VarCharVector varCharVector = new VarCharVector("variable-size-primitive-layout", allocator); + ){ + varCharVector.allocateNew(3); + varCharVector.set(0, "one".getBytes()); + varCharVector.set(1, "two".getBytes()); + varCharVector.set(2, "three".getBytes()); + varCharVector.setValueCount(3); + System.out.println("Vector created in memory: " + varCharVector); + } + +.. code-block:: shell + + Vector created in memory: [one, two, three] + +Create a Field +************** + +**Fields** are used to denote the particular columns of tabular data. +They consist of a name, a data type, a flag indicating whether the column can have null values, +and optional key-value metadata. + +Example: create a field named "document" of string type: + +.. code-block:: Java + + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + import java.util.HashMap; + import java.util.Map; + + Map metadata = new HashMap<>(); + metadata.put("A", "Id card"); + metadata.put("B", "Passport"); + metadata.put("C", "Visa"); + Field document = new Field("document", + new FieldType(true, new ArrowType.Utf8(), /*dictionary*/ null, metadata), + /*children*/ null); + System.out.println("Field created: " + document + ", Metadata: " + document.getMetadata()); + +.. code-block:: shell + + Field created: document: Utf8, Metadata: {A=Id card, B=Passport, C=Visa} + +Create a Schema +*************** + +**Schemas** hold a sequence of fields together with some optional metadata. + +Example: Create a schema describing datasets with two columns: +an int32 column "A" and a UTF8-encoded string column "B" + +.. code-block:: Java + + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + import org.apache.arrow.vector.types.pojo.Schema; + import java.util.HashMap; + import java.util.Map; + import static java.util.Arrays.asList; + + Map metadata = new HashMap<>(); + metadata.put("K1", "V1"); + metadata.put("K2", "V2"); + Field a = new Field("A", FieldType.nullable(new ArrowType.Int(32, true)), /*children*/ null); + Field b = new Field("B", FieldType.nullable(new ArrowType.Utf8()), /*children*/ null); + Schema schema = new Schema(asList(a, b), metadata); + System.out.println("Schema created: " + schema); + +.. code-block:: shell + + Schema created: Schema(metadata: {K1=V1, K2=V2}) + +Create a VectorSchemaRoot +************************* + +A **VectorSchemaRoot** combines ValueVectors with a Schema to represent tabular data. + +Example: Create a dataset of names (strings) and ages (32-bit signed integers). + +.. code-block:: Java + + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.IntVector; + import org.apache.arrow.vector.VarCharVector; + import org.apache.arrow.vector.VectorSchemaRoot; + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + import org.apache.arrow.vector.types.pojo.Schema; + import java.nio.charset.StandardCharsets; + import java.util.HashMap; + import java.util.Map; + import static java.util.Arrays.asList; + + Field age = new Field("age", + FieldType.nullable(new ArrowType.Int(32, true)), + /*children*/null + ); + Field name = new Field("name", + FieldType.nullable(new ArrowType.Utf8()), + /*children*/null + ); + Schema schema = new Schema(asList(age, name), /*metadata*/ null); + try( + BufferAllocator allocator = new RootAllocator(); + VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator); + IntVector ageVector = (IntVector) root.getVector("age"); + VarCharVector nameVector = (VarCharVector) root.getVector("name"); + ){ + ageVector.allocateNew(3); + ageVector.set(0, 10); + ageVector.set(1, 20); + ageVector.set(2, 30); + nameVector.allocateNew(3); + nameVector.set(0, "Dave".getBytes(StandardCharsets.UTF_8)); + nameVector.set(1, "Peter".getBytes(StandardCharsets.UTF_8)); + nameVector.set(2, "Mary".getBytes(StandardCharsets.UTF_8)); + root.setRowCount(3); + System.out.println("VectorSchemaRoot created: \n" + root.contentToTSVString()); + } + +.. code-block:: shell + + VectorSchemaRoot created: + age name + 10 Dave + 20 Peter + 30 Mary + + +Interprocess Communication (IPC) +******************************** + +Arrow data can be written to and read from disk, and both of these can be done in +a streaming and/or random-access fashion depending on application requirements. + +**Write data to an arrow file** + +Example: Write the dataset from the previous example to an Arrow IPC file (random-access). + +.. code-block:: Java + + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.IntVector; + import org.apache.arrow.vector.VarCharVector; + import org.apache.arrow.vector.VectorSchemaRoot; + import org.apache.arrow.vector.ipc.ArrowFileWriter; + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + import org.apache.arrow.vector.types.pojo.Schema; + import java.io.File; + import java.io.FileOutputStream; + import java.io.IOException; + import java.nio.charset.StandardCharsets; + import java.util.HashMap; + import java.util.Map; + import static java.util.Arrays.asList; + + Field age = new Field("age", + FieldType.nullable(new ArrowType.Int(32, true)), + /*children*/ null); + Field name = new Field("name", + FieldType.nullable(new ArrowType.Utf8()), + /*children*/ null); + Schema schema = new Schema(asList(age, name)); + try( + BufferAllocator allocator = new RootAllocator(); + VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator); + IntVector ageVector = (IntVector) root.getVector("age"); + VarCharVector nameVector = (VarCharVector) root.getVector("name"); + ){ + ageVector.allocateNew(3); + ageVector.set(0, 10); + ageVector.set(1, 20); + ageVector.set(2, 30); + nameVector.allocateNew(3); + nameVector.set(0, "Dave".getBytes(StandardCharsets.UTF_8)); + nameVector.set(1, "Peter".getBytes(StandardCharsets.UTF_8)); + nameVector.set(2, "Mary".getBytes(StandardCharsets.UTF_8)); + root.setRowCount(3); + File file = new File("random_access_file.arrow"); + try ( + FileOutputStream fileOutputStream = new FileOutputStream(file); + ArrowFileWriter writer = new ArrowFileWriter(root, /*provider*/ null, fileOutputStream.getChannel()); + ) { + writer.start(); + writer.writeBatch(); + writer.end(); + System.out.println("Record batches written: " + writer.getRecordBlocks().size() + + ". Number of rows written: " + root.getRowCount()); + } catch (IOException e) { + e.printStackTrace(); + } + } + +.. code-block:: shell + + Record batches written: 1. Number of rows written: 3 + +**Read data from an arrow file** + +Example: Read the dataset from the previous example from an Arrow IPC file (random-access). + +.. code-block:: Java + + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.ipc.ArrowFileReader; + import org.apache.arrow.vector.ipc.message.ArrowBlock; + import org.apache.arrow.vector.VectorSchemaRoot; + import java.io.File; + import java.io.FileInputStream; + import java.io.FileOutputStream; + import java.io.IOException; + + try( + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + FileInputStream fileInputStream = new FileInputStream(new File("random_access_file.arrow")); + ArrowFileReader reader = new ArrowFileReader(fileInputStream.getChannel(), allocator); + ){ + System.out.println("Record batches in file: " + reader.getRecordBlocks().size()); + for (ArrowBlock arrowBlock : reader.getRecordBlocks()) { + reader.loadRecordBatch(arrowBlock); + VectorSchemaRoot root = reader.getVectorSchemaRoot(); + System.out.println("VectorSchemaRoot read: \n" + root.contentToTSVString()); + } + } catch (IOException e) { + e.printStackTrace(); + } + +.. code-block:: shell + + Record batches in file: 1 + VectorSchemaRoot read: + age name + 10 Dave + 20 Peter + 30 Mary + +More examples available at `Arrow Java Cookbook`_. + +.. _`Arrow Java Cookbook`: https://arrow.apache.org/cookbook/java diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst new file mode 100644 index 00000000..523ac0c7 --- /dev/null +++ b/docs/source/reference/index.rst @@ -0,0 +1,21 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +Java Reference (javadoc) +======================== + +Stub page for the Java reference docs; actual source is located in the java/ directory. diff --git a/docs/source/substrait.rst b/docs/source/substrait.rst new file mode 100644 index 00000000..b3678ac8 --- /dev/null +++ b/docs/source/substrait.rst @@ -0,0 +1,201 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +========= +Substrait +========= + +The ``arrow-dataset`` module can execute Substrait_ plans via the :external+arrow:doc:`Acero ` +query engine. + +Executing Queries Using Substrait Plans +======================================= + +Plans can reference data in files via URIs, or "named tables" that must be provided along with the plan. + +Here is an example of a Java program that queries a Parquet file using Java Substrait +(this example use `Substrait Java`_ project to compile a SQL query to a Substrait plan): + +.. code-block:: Java + + import com.google.common.collect.ImmutableList; + import io.substrait.isthmus.SqlToSubstrait; + import io.substrait.proto.Plan; + import org.apache.arrow.dataset.file.FileFormat; + import org.apache.arrow.dataset.file.FileSystemDatasetFactory; + import org.apache.arrow.dataset.jni.NativeMemoryPool; + import org.apache.arrow.dataset.scanner.ScanOptions; + import org.apache.arrow.dataset.scanner.Scanner; + import org.apache.arrow.dataset.source.Dataset; + import org.apache.arrow.dataset.source.DatasetFactory; + import org.apache.arrow.dataset.substrait.AceroSubstraitConsumer; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.ipc.ArrowReader; + import org.apache.calcite.sql.parser.SqlParseException; + + import java.nio.ByteBuffer; + import java.util.HashMap; + import java.util.Map; + + public class ClientSubstrait { + public static void main(String[] args) { + String uri = "file:///data/tpch_parquet/nation.parquet"; + ScanOptions options = new ScanOptions(/*batchSize*/ 32768); + try ( + BufferAllocator allocator = new RootAllocator(); + DatasetFactory datasetFactory = new FileSystemDatasetFactory(allocator, NativeMemoryPool.getDefault(), + FileFormat.PARQUET, uri); + Dataset dataset = datasetFactory.finish(); + Scanner scanner = dataset.newScan(options); + ArrowReader reader = scanner.scanBatches() + ) { + // map table to reader + Map mapTableToArrowReader = new HashMap<>(); + mapTableToArrowReader.put("NATION", reader); + // get binary plan + Plan plan = getPlan(); + ByteBuffer substraitPlan = ByteBuffer.allocateDirect(plan.toByteArray().length); + substraitPlan.put(plan.toByteArray()); + // run query + try (ArrowReader arrowReader = new AceroSubstraitConsumer(allocator).runQuery( + substraitPlan, + mapTableToArrowReader + )) { + while (arrowReader.loadNextBatch()) { + System.out.println(arrowReader.getVectorSchemaRoot().contentToTSVString()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + static Plan getPlan() throws SqlParseException { + String sql = "SELECT * from nation"; + String nation = "CREATE TABLE NATION (N_NATIONKEY BIGINT NOT NULL, N_NAME CHAR(25), " + + "N_REGIONKEY BIGINT NOT NULL, N_COMMENT VARCHAR(152))"; + SqlToSubstrait sqlToSubstrait = new SqlToSubstrait(); + Plan plan = sqlToSubstrait.execute(sql, ImmutableList.of(nation)); + return plan; + } + } + +.. code-block:: text + + // Results example: + FieldPath(0) FieldPath(1) FieldPath(2) FieldPath(3) + 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 + +Executing Projections and Filters Using Extended Expressions +============================================================ + +Dataset also supports projections and filters with Substrait's `Extended Expression`_. +This requires the substrait-java library. + +This Java program: + +- Loads a Parquet file containing the "nation" table from the TPC-H benchmark. +- Applies a filter: + - ``N_NATIONKEY > 18`` +- Projects two new columns: + - ``N_REGIONKEY + 10`` + - ``N_NAME || ' - ' || N_COMMENT`` + + + +.. code-block:: Java + + import com.google.common.collect.ImmutableList; + import io.substrait.isthmus.SqlExpressionToSubstrait; + import io.substrait.proto.ExtendedExpression; + import org.apache.arrow.dataset.file.FileFormat; + import org.apache.arrow.dataset.file.FileSystemDatasetFactory; + import org.apache.arrow.dataset.jni.NativeMemoryPool; + import org.apache.arrow.dataset.scanner.ScanOptions; + import org.apache.arrow.dataset.scanner.Scanner; + import org.apache.arrow.dataset.source.Dataset; + import org.apache.arrow.dataset.source.DatasetFactory; + import org.apache.arrow.memory.BufferAllocator; + import org.apache.arrow.memory.RootAllocator; + import org.apache.arrow.vector.ipc.ArrowReader; + import org.apache.calcite.sql.parser.SqlParseException; + + import java.nio.ByteBuffer; + import java.util.Base64; + import java.util.Optional; + + public class ClientSubstraitExtendedExpressionsCookbook { + + public static void main(String[] args) throws SqlParseException { + projectAndFilterDataset(); + } + + private static void projectAndFilterDataset() throws SqlParseException { + String uri = "file:///Users/data/tpch_parquet/nation.parquet"; + ScanOptions options = + new ScanOptions.Builder(/*batchSize*/ 32768) + .columns(Optional.empty()) + .substraitFilter(getByteBuffer(new String[]{"N_NATIONKEY > 18"})) + .substraitProjection(getByteBuffer(new String[]{"N_REGIONKEY + 10", + "N_NAME || CAST(' - ' as VARCHAR) || N_COMMENT"})) + .build(); + try (BufferAllocator allocator = new RootAllocator(); + DatasetFactory datasetFactory = + new FileSystemDatasetFactory( + allocator, NativeMemoryPool.getDefault(), FileFormat.PARQUET, uri); + Dataset dataset = datasetFactory.finish(); + Scanner scanner = dataset.newScan(options); + ArrowReader reader = scanner.scanBatches()) { + while (reader.loadNextBatch()) { + System.out.println(reader.getVectorSchemaRoot().contentToTSVString()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static ByteBuffer getByteBuffer(String[] sqlExpression) throws SqlParseException { + String schema = + "CREATE TABLE NATION (N_NATIONKEY INT NOT NULL, N_NAME VARCHAR, " + + "N_REGIONKEY INT NOT NULL, N_COMMENT VARCHAR)"; + SqlExpressionToSubstrait expressionToSubstrait = new SqlExpressionToSubstrait(); + ExtendedExpression expression = + expressionToSubstrait.convert(sqlExpression, ImmutableList.of(schema)); + byte[] expressionToByte = + Base64.getDecoder().decode(Base64.getEncoder().encodeToString(expression.toByteArray())); + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(expressionToByte.length); + byteBuffer.put(expressionToByte); + return byteBuffer; + } + } + +.. code-block:: text + + column-1 column-2 + 13 ROMANIA - ular asymptotes are about the furious multipliers. express dependencies nag above the ironically ironic account + 14 SAUDI ARABIA - ts. silent requests haggle. closely express packages sleep across the blithely + 12 VIETNAM - hely enticingly express accounts. even, final + 13 RUSSIA - requests against the platelets use never according to the quickly regular pint + 13 UNITED KINGDOM - eans boost carefully special requests. accounts are. carefull + 11 UNITED STATES - y final packages. slow foxes cajole quickly. quickly silent platelets breach ironic accounts. unusual pinto be + +.. _`Substrait`: https://substrait.io/ +.. _`Substrait Java`: https://github.com/substrait-io/substrait-java +.. _`Acero`: https://arrow.apache.org/docs/cpp/streaming_execution.html +.. _`Extended Expression`: https://github.com/substrait-io/substrait/blob/main/site/docs/expressions/extended_expression.md diff --git a/docs/source/table.rst b/docs/source/table.rst new file mode 100644 index 00000000..5aa95e15 --- /dev/null +++ b/docs/source/table.rst @@ -0,0 +1,378 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +===== +Table +===== + +**NOTE**: The Table API is experimental and subject to change. See the list of limitations below. + +`Table`_ is an immutable tabular data structure based on `FieldVector`_. Like `VectorSchemaRoot`_, ``Table`` is a columnar data structure backed by Arrow arrays, or more specifically, by ``FieldVector`` objects. It differs from ``VectorSchemaRoot`` mainly in that it is fully immutable and lacks support for batch operations. Anyone processing batches of tabular data in a pipeline should continue to use ``VectorSchemaRoot``. Finally, the ``Table`` API is mainly row-oriented, so in some ways it's more like the JDBC API than the ``VectorSchemaRoot`` API, but you can still use ``FieldReaders`` to work with data in a columnar fashion. + +Mutation in Table and VectorSchemaRoot +====================================== + +``VectorSchemaRoot`` provides a thin wrapper on the vectors that hold its data. Individual vectors can be retrieved from a vector schema root. These vectors have *setters* for modifying their elements, making ``VectorSchemaRoot`` immutable only by convention. The protocol for mutating a vector is documented in the `ValueVector`_ interface: + +- values need to be written in order (e.g. index 0, 1, 2, 5) +- null vectors start with all values as null before writing anything +- for variable width types, the offset vector should be all zeros before writing +- you must call setValueCount before a vector can be read +- you should never write to a vector once it has been read. + +The rules aren't enforced by the API so the programmer is responsible for ensuring that they are followed. Failure to do so could lead to runtime exceptions. + +``Table``, on the other hand, is immutable. The underlying vectors are not exposed. When a table is created from existing vectors, their memory is transferred to new vectors, so subsequent changes to the original vectors can't impact the new table's values. + +Features and limitations +====================================== + +A basic set of table functionality is currently available: + +- Create a table from vectors or ``VectorSchemaRoot`` +- Iterate tables by row, or set the current row index directly +- Access vector values as primitives, objects, and/or nullable `ValueHolder`_ instances (depending on type) +- Get a ``FieldReader`` for any vector +- Add and remove vectors, creating new tables +- Encode and decode a table's vectors using dictionary encoding +- Export table data for use by native code +- Print representative data to TSV strings +- Get a table's schema +- Slice tables +- Convert table to ``VectorSchemaRoot`` + +Limitations in the 11.0.0 release: + +- No support ``ChunkedArray`` or any form of row-group. Support for chunked arrays or row groups will be considered for a future release. +- No support for the C-Stream API. Support for the streaming API is contingent on chunked array support +- No support for creating tables directly from Java POJOs. All data held by a table must be imported via a ``VectorSchemaRoot``, or from collections or arrays of vectors. + +The Table API +============= + +Like ``VectorSchemaRoot``, a table contains a `Schema`_ and an ordered collection of ``FieldVector`` objects, but it is designed to be accessed via a row-oriented interface. + +Creating a Table from a VectorSchemaRoot +**************************************** + +Tables are created from a ``VectorSchemaRoot`` as shown below. The memory buffers holding the data are transferred from the vector schema root to new vectors in the new table, clearing the source vectors in the process. This ensures that the data in your new table is never changed. Since the buffers are transferred rather than copied, this is a very low overhead operation. + +.. code-block:: Java + + Table t = new Table(someVectorSchemaRoot); + +If you now update the vectors held by the ``VectorSchemaRoot`` (using some version of ``ValueVector#setSafe()``), it would reflect those changes, but the values in table *t* are unchanged. + +Creating a Table from FieldVectors +********************************** + +Tables can be created from ``FieldVectors`` as shown below, using 'var-arg' array arguments: + +.. code-block:: Java + + IntVector myVector = createMyIntVector(); + VectorSchemaRoot vsr1 = new VectorSchemaRoot(myVector); + +or by passing a collection: + +.. code-block:: Java + + IntVector myVector = createMyIntVector(); + List fvList = List.of(myVector); + VectorSchemaRoot vsr1 = new VectorSchemaRoot(fvList); + +It is rarely a good idea to share vectors between multiple vector schema roots, and it would not be a good idea to share them between vector schema roots and tables. Creating a ``VectorSchemaRoot`` from a list of vectors does not cause the reference counts for the vectors to be incremented. Unless you manage the counts manually, the code below would lead to more references than reference counts, and that could lead to trouble. There is an implicit assumption that the vectors were created for use by *one* ``VectorSchemaRoot`` that this code violates. + +*Don't do this:* + +.. code-block:: Java + + IntVector myVector = createMyIntVector(); // Reference count for myVector = 1 + VectorSchemaRoot vsr1 = new VectorSchemaRoot(myVector); // Still one reference + VectorSchemaRoot vsr2 = new VectorSchemaRoot(myVector); + // Ref count is still one, but there are two VSRs with a reference to myVector + vsr2.clear(); // Reference count for myVector is 0. + +What is happening is that the reference counter works at a lower level than the ``VectorSchemaRoot`` interface. A reference counter counts references to `ArrowBuf`_ instances that control memory buffers. It doesn't count references to the vectors that hold those ArrowBufs. In the example above, each ``ArrowBuf`` is held by one vector, so there is only one reference. This distinction is blurred when you call the ``VectorSchemaRoot``'s clear() method, which frees the memory held by each of the vectors it references even though another instance references the same vectors. + +When you create tables from vectors, it's assumed that there are no external references to those vectors. To be certain, the buffers underlying these vectors are transferred to new vectors in the new table, and the original vectors are cleared. + +*Don't do this either, but note the difference from above:* + +.. code-block:: Java + + IntVector myVector = createMyIntVector(); // Reference count for myVector = 1 + Table t1 = new Table(myVector); + // myVector is cleared; Table t1 has a new hidden vector with the data from myVector + Table t2 = new Table(myVector); + // t2 has no rows because myVector was just cleared + // t1 continues to have the data from the original vector + t2.clear(); + // no change because t2 is already empty and t1 is independent + +With tables, memory is explicitly transferred on instantiation so the buffers held by a table are held by *only* that table. + +Creating Tables with dictionary-encoded vectors +*********************************************** + +Another point of difference is that ``VectorSchemaRoot`` is uninformed about any dictionary-encoding of its vectors, while tables hold an optional `DictionaryProvider`_ instance. If any vectors in the source data are encoded, a DictionaryProvider must be set to un-encode the values. + +.. code-block:: Java + + VectorSchemaRoot vsr = myVsr(); + DictionaryProvider provider = myProvider(); + Table t = new Table(vsr, provider); + +In ``Table``, dictionaries are used like they are with vectors. To decode a vector, the user provides the name of the vector to decode and the dictionary id: + +.. code-block:: Java + + Table t = new Table(vsr, provider); + ValueVector decodedName = t.decode("name", 1L); + +To encode a vector from a table, a similar approach is used: + +.. code-block:: Java + + Table t = new Table(vsr, provider); + ValueVector encodedName = t.encode("name", 1L); + +Freeing memory explicitly +************************* + +Tables use off-heap memory that must be freed when it is no longer needed. ``Table`` implements ``AutoCloseable`` so the best way to create one is in a try-with-resources block: + +.. code-block:: Java + + try (VectorSchemaRoot vsr = myMethodForGettingVsrs(); + Table t = new Table(vsr)) { + // do useful things. + } + +If you don't use a try-with-resources block, you must close the table manually: + +.. code-block:: Java + + try { + VectorSchemaRoot vsr = myMethodForGettingVsrs(); + Table t = new Table(vsr); + // do useful things. + } finally { + vsr.close(); + t.close(); + } + +Manual closing should be performed in a finally block. + +Getting the schema +****************** + +You get the table's schema just as you would with a vector schema root: + +.. code-block:: Java + + Schema s = table.getSchema(); + +Adding and removing vectors +*************************** + +``Table`` provides facilities for adding and removing vectors modeled on the same functionality in ``VectorSchemaRoot``. These operations return new instances rather than modifying the original instance in-place. + +.. code-block:: Java + + try (Table t = new Table(vectorList)) { + IntVector v3 = new IntVector("3", intFieldType, allocator); + Table t2 = t.addVector(2, v3); + Table t3 = t2.removeVector(1); + // don't forget to close t2 and t3 + } + +Slicing tables +************** + +``Table`` supports *slice()* operations, where a slice of a source table is a second Table that refers to a single, contiguous range of rows in the source. + +.. code-block:: Java + + try (Table t = new Table(vectorList)) { + Table t2 = t.slice(100, 200); // creates a slice referencing the values in range (100, 200] + ... + } + +This raises the question: If you create a slice with *all* the values in the source table (as shown below), how would that differ from a new Table constructed with the same vectors as the source? + +.. code-block:: Java + + try (Table t = new Table(vectorList)) { + Table t2 = t.slice(0, t.getRowCount()); // creates a slice referencing all the values in t + // ... + } + +The difference is that when you *construct* a new table, the buffers are transferred from the source vectors to new vectors in the destination. With a slice, both tables share the same underlying vectors. That's OK, though, since both tables are immutable. + +Using FieldReaders +****************** + +You can get a `FieldReader`_ for any vector in the Table passing either the `Field`_, vector index, or vector name as an argument. The signatures are the same as in ``VectorSchemaRoot``. + +.. code-block:: Java + + FieldReader nameReader = table.getReader("user_name"); + +Row operations +************** + +Row-based access is supported by the `Row`_ object. ``Row`` provides *get()* methods by both vector name and vector position, but no *set()* operations. + +It is important to recognize that rows are NOT reified as objects, but rather operate like a cursor where the data from numerous logical rows in the table can be viewed (one at a time) using the same ``Row`` instance. See "Moving from row-to-row" below for information about navigating through the table. + +Getting a row +************* + +Calling ``immutableRow()`` on any table instance returns a new ``Row`` instance. + +.. code-block:: Java + + Row r = table.immutableRow(); + +Moving from row-to-row +********************** + +Since rows are iterable, you can traverse a table using a standard while loop: + +.. code-block:: Java + + Row r = table.immutableRow(); + while (r.hasNext()) { + r.next(); + // do something useful here + } + +``Table`` implements ``Iterable`` so you can access rows directly from a table in an enhanced *for* loop: + +.. code-block:: Java + + for (Row row: table) { + int age = row.getInt("age"); + boolean nameIsNull = row.isNull("name"); + ... + } + +Finally, while rows are usually iterated in the order of the underlying data vectors, but they are also positionable using the ``Row#setPosition()`` method, so you can skip to a specific row. Row numbers are 0-based. + +.. code-block:: Java + + Row r = table.immutableRow(); + int age101 = r.setPosition(101); // change position directly to 101 + +Any changes to position are applied to all the columns in the table. + +Note that you must call ``next()``, or ``setPosition()`` before accessing values via a row. Failure to do so results in a runtime exception. + +Read operations using rows +************************** + +Methods are available for getting values by vector name and vector index, where index is the 0-based position of the vector in the table. For example, assuming 'age' is the 13th vector in 'table', the following two gets are equivalent: + +.. code-block:: Java + + Row r = table.immutableRow(); + r.next(); // position the row at the first value + int age1 = r.get("age"); // gets the value of vector named 'age' in the table at row 0 + int age2 = r.get(12); // gets the value of the 13th vector in the table at row 0 + +You can also get value using a nullable ``ValueHolder``. For example: + +.. code-block:: Java + + NullableIntHolder holder = new NullableIntHolder(); + int b = row.getInt("age", holder); + +This can be used to retrieve values without creating a new Object for each. + +In addition to getting values, you can check if a value is null using ``isNull()``. This is important if the vector contains any nulls, as asking for a value from a vector can cause NullPointerExceptions in some cases. + +.. code-block:: Java + + boolean name0isNull = row.isNull("name"); + +You can also get the current row number: + +.. code-block:: Java + + int row = row.getRowNumber(); + +Reading values as Objects +************************* + +For any given vector type, the basic *get()* method returns a primitive value wherever possible. For example, *getTimeStampMicro()* returns a long value that encodes the timestamp. To get the LocalDateTime object representing that timestamp in Java, another method with 'Obj' appended to the name is provided. For example: + +.. code-block:: Java + + long ts = row.getTimeStampMicro(); + LocalDateTime tsObject = row.getTimeStampMicroObj(); + +The exception to this naming scheme is for complex vector types (List, Map, Schema, Union, DenseUnion, and ExtensionType). These always return objects rather than primitives so no "Obj" extension is required. It is expected that some users may subclass ``Row`` to add getters that are more specific to their needs. + +Reading VarChars and LargeVarChars +********************************** + +Strings in arrow are represented as byte arrays encoded with the UTF-8 charset. You can get either a String result or the actual byte array. + +.. code-block:: Java + + byte[] b = row.getVarChar("first_name"); + String s = row.getVarCharObj("first_name"); // uses the default encoding (UTF-8) + +Converting a Table to a VectorSchemaRoot +**************************************** + +Tables can be converted to vector schema roots using the *toVectorSchemaRoot()* method. Buffers are transferred to the vector schema root and the source table is cleared. + +.. code-block:: Java + + VectorSchemaRoot root = myTable.toVectorSchemaRoot(); + +Working with the C-Data interface +********************************* + +The ability to work with native code is required for many Arrow features. This section describes how tables can be be exported for use with native code + +Exporting works by converting the data to a ``VectorSchemaRoot`` instance and using the existing facilities to transfer the data. You could do it yourself, but that isn't ideal because conversion to a vector schema root breaks the immutability guarantees. Using the ``exportTable()`` methods in the `Data`_ class avoids this concern. + +.. code-block:: Java + + Data.exportTable(bufferAllocator, table, dictionaryProvider, outArrowArray); + +If the table contains dictionary-encoded vectors and was constructed with a ``DictionaryProvider``, the provider argument to ``exportTable()`` can be omitted and the table's provider attribute will be used: + +.. code-block:: Java + + Data.exportTable(bufferAllocator, table, outArrowArray); + +.. _`ArrowBuf`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/memory/ArrowBuf.html +.. _`Data`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/c/Data.html +.. _`DictionaryProvider`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/dictionary/DictionaryProvider.html +.. _`Field`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/types/pojo/Field.html +.. _`FieldReader`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/complex/reader/FieldReader.html +.. _`FieldVector`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/FieldVector.html +.. _`Row`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/table/Row.html +.. _`Schema`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/types/pojo/Schema.html +.. _`Table`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/table/Table.html +.. _`ValueHolder`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/holders/ValueHolder.html +.. _`ValueVector`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/ValueVector.html +.. _`VectorSchemaRoot`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/VectorSchemaRoot.html diff --git a/docs/source/vector.rst b/docs/source/vector.rst new file mode 100644 index 00000000..19962774 --- /dev/null +++ b/docs/source/vector.rst @@ -0,0 +1,366 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +=========== +ValueVector +=========== + +:class:`ValueVector` interface (which called Array in C++ implementation and +the :external+arrow:doc:`the specification `) is an abstraction that is used to store a +sequence of values having the same type in an individual column. Internally, those values are +represented by one or several buffers, the number and meaning of which depend on the vector’s data type. + +There are concrete subclasses of :class:`ValueVector` for each primitive data type +and nested type described in the specification. There are a few differences in naming +with the type names described in the specification: +Table with non-intuitive names (BigInt = 64 bit integer, etc). + +It is important that vector is allocated before attempting to read or write, +:class:`ValueVector` "should" strive to guarantee this order of operation: +create > allocate > mutate > set value count > access > clear (or allocate to start the process over). +We will go through a concrete example to demonstrate each operation in the next section. + +Vector Life Cycle +================= + +As discussed above, each vector goes through several steps in its life cycle, +and each step is triggered by a vector operation. In particular, we have the following vector operations: + +1. **Vector creation**: we create a new vector object by, for example, the vector constructor. +The following code creates a new ``IntVector`` by the constructor: + +.. code-block:: Java + + RootAllocator allocator = new RootAllocator(Long.MAX_VALUE); + ... + IntVector vector = new IntVector("int vector", allocator); + +By now, a vector object is created. However, no underlying memory has been allocated, so we need the +following step. + +2. **Vector allocation**: in this step, we allocate memory for the vector. For most vectors, we +have two options: 1) if we know the maximum vector capacity, we can specify it by calling the +``allocateNew(int)`` method; 2) otherwise, we should call the ``allocateNew()`` method, and a default +capacity will be allocated for it. For our running example, we assume that the vector capacity never +exceeds 10: + +.. code-block:: Java + + vector.allocateNew(10); + +3. **Vector mutation**: now we can populate the vector with values we desire. For all vectors, we can populate +vector values through vector writers (An example will be given in the next section). For primitive types, +we can also mutate the vector by the set methods. There are two classes of set methods: 1) if we can +be sure the vector has enough capacity, we can call the ``set(index, value)`` method. 2) if we are not sure +about the vector capacity, we should call the ``setSafe(index, value)`` method, which will automatically +take care of vector reallocation, if the capacity is not sufficient. For our running example, we know the +vector has enough capacity, so we can call + +.. code-block:: Java + + vector.set(/*index*/5, /*value*/25); + +4. **Set value count**: for this step, we set the value count of the vector by calling the +``setValueCount(int)`` method: + +.. code-block:: Java + + vector.setValueCount(10); + +After this step, the vector enters an immutable state. In other words, we should no longer mutate it. +(Unless we reuse the vector by allocating it again. This will be discussed shortly.) + +5. **Vector access**: it is time to access vector values. Similarly, we have two options to access values: +1) get methods and 2) vector reader. Vector reader works for all types of vectors, while get methods are +only available for primitive vectors. A concrete example for vector reader will be given in the next section. +Below is an example of vector access by get method: + +.. code-block:: Java + + int value = vector.get(5); // value == 25 + +6. **Vector clear**: when we are done with the vector, we should clear it to release its memory. This is done by +calling the ``close()`` method: + +.. code-block:: Java + + vector.close(); + +Some points to note about the steps above: + +* The steps are not necessarily performed in a linear sequence. Instead, they can be in a loop. For example, + when a vector enters the access step, we can also go back to the vector mutation step, and then set value + count, access vector, and so on. + +* We should try to make sure the above steps are carried out in order. Otherwise, the vector + may be in an undefined state, and some unexpected behavior may occur. However, this restriction + is not strict. That means it is possible that we violates the order above, but still get + correct results. + +* When mutating vector values through set methods, we should prefer ``set(index, value)`` methods to + ``setSafe(index, value)`` methods whenever possible, to avoid unnecessary performance overhead of handling + vector capacity. + +* All vectors implement the ``AutoCloseable`` interface. So they must be closed explicitly when they are + no longer used, to avoid resource leak. To make sure of this, it is recommended to place vector related operations + into a try-with-resources block. + +* For fixed width vectors (e.g. IntVector), we can set values at different indices in arbitrary orders. + For variable width vectors (e.g. VarCharVector), however, we must set values in non-decreasing order of the + indices. Otherwise, the values after the set position will become invalid. For example, suppose we use the + following statements to populate a variable width vector: + +.. code-block:: Java + + VarCharVector vector = new VarCharVector("vector", allocator); + vector.allocateNew(); + vector.setSafe(0, "zero"); + vector.setSafe(1, "one"); + ... + vector.setSafe(9, "nine"); + +Then we set the value at position 5 again: + +.. code-block:: Java + + vector.setSafe(5, "5"); + +After that, the values at positions 6, 7, 8, and 9 of the vector will become invalid. + +Building ValueVector +==================== + +Note that the current implementation doesn't enforce the rule that Arrow objects are immutable. +:class:`ValueVector` instances could be created directly by using new keyword, there are +set/setSafe APIs and concrete subclasses of FieldWriter for populating values. + +For example, the code below shows how to build a :class:`BigIntVector`, in this case, we build a +vector of the range 0 to 7 where the element that should hold the fourth value is nulled + +.. code-block:: Java + + try (BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + BigIntVector vector = new BigIntVector("vector", allocator)) { + vector.allocateNew(8); + vector.set(0, 1); + vector.set(1, 2); + vector.set(2, 3); + vector.setNull(3); + vector.set(4, 5); + vector.set(5, 6); + vector.set(6, 7); + vector.set(7, 8); + vector.setValueCount(8); // this will finalizes the vector by convention. + ... + } + +The :class:`BigIntVector` holds two ArrowBufs. The first buffer holds the null bitmap, which consists +here of a single byte with the bits 1|1|1|1|0|1|1|1 (the bit is 1 if the value is non-null). +The second buffer contains all the above values. As the fourth entry is null, the value at that position +in the buffer is undefined. Note compared with set API, setSafe API would check value capacity before setting +values and reallocate buffers if necessary. + +Here is how to build a vector using writer + +.. code-block:: Java + + try (BigIntVector vector = new BigIntVector("vector", allocator); + BigIntWriter writer = new BigIntWriterImpl(vector)) { + writer.setPosition(0); + writer.writeBigInt(1); + writer.setPosition(1); + writer.writeBigInt(2); + writer.setPosition(2); + writer.writeBigInt(3); + // writer.setPosition(3) is not called which means the fourth value is null. + writer.setPosition(4); + writer.writeBigInt(5); + writer.setPosition(5); + writer.writeBigInt(6); + writer.setPosition(6); + writer.writeBigInt(7); + writer.setPosition(7); + writer.writeBigInt(8); + } + +There are get API and concrete subclasses of :class:`FieldReader` for accessing vector values, what needs +to be declared is that writer/reader is not as efficient as direct access + +.. code-block:: Java + + // access via get API + for (int i = 0; i < vector.getValueCount(); i++) { + if (!vector.isNull(i)) { + System.out.println(vector.get(i)); + } + } + + // access via reader + BigIntReader reader = vector.getReader(); + for (int i = 0; i < vector.getValueCount(); i++) { + reader.setPosition(i); + if (reader.isSet()) { + System.out.println(reader.readLong()); + } + } + +Building ListVector +=================== + +A :class:`ListVector` is a vector that holds a list of values for each index. Working with one you need to handle the same steps as mentioned above (create > allocate > mutate > set value count > access > clear), but the details of how you accomplish this are slightly different since you need to both create the vector and set the list of values for each index. + +For example, the code below shows how to build a :class:`ListVector` of int's using the writer :class:`UnionListWriter`. We build a vector from 0 to 9 and each index contains a list with values [[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 6, 8], …, [0, 9, 18, 27, 36]]. List values can be added in any order so writing a list such as [3, 1, 2] would be just as valid. + +.. code-block:: Java + + try (BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + ListVector listVector = ListVector.empty("vector", allocator)) { + UnionListWriter writer = listVector.getWriter(); + for (int i = 0; i < 10; i++) { + writer.startList(); + writer.setPosition(i); + for (int j = 0; j < 5; j++) { + writer.writeInt(j * i); + } + writer.setValueCount(5); + writer.endList(); + } + listVector.setValueCount(10); + } + +:class:`ListVector` values can be accessed either through the get API or through the reader class :class:`UnionListReader`. To read all the values, first enumerate through the indexes, and then enumerate through the inner list values. + +.. code-block:: Java + + // access via get API + for (int i = 0; i < listVector.getValueCount(); i++) { + if (!listVector.isNull(i)) { + ArrayList elements = (ArrayList) listVector.getObject(i); + for (Integer element : elements) { + System.out.println(element); + } + } + } + + // access via reader + UnionListReader reader = listVector.getReader(); + for (int i = 0; i < listVector.getValueCount(); i++) { + reader.setPosition(i); + while (reader.next()) { + IntReader intReader = reader.reader(); + if (intReader.isSet()) { + System.out.println(intReader.readInteger()); + } + } + } + +Dictionary Encoding +=================== + +Dictionary encoding is a form of compression where values of one type are replaced by values of a smaller type: an array of ints replacing an array of strings is a common example. The mapping between the original values and the replacements is held in a 'dictionary'. Since the dictionary needs only one copy of each of the longer values, the combination of the dictionary and the array of smaller values may use less memory. The more repetitive the original data, the greater the savings. + +A ``FieldVector`` can be dictionary encoded for performance or improved memory efficiency. Nearly any type of vector might be encoded if there are many values, but few unique values. + +There are a few steps involved in the encoding process: + +1. Create a regular, un-encoded vector and populate it +2. Create a dictionary vector of the same type as the un-encoded vector. This vector must have the same values, but each unique value in the un-encoded vector need appear here only once. +3. Create a ``Dictionary``. It will contain the dictionary vector, plus a ``DictionaryEncoding`` object that holds the encoding's metadata and settings values. +4. Create a ``DictionaryEncoder``. +5. Call the encode() method on the ``DictionaryEncoder`` to produce an encoded version of the original vector. +6. (Optional) Call the decode() method on the encoded vector to re-create the original values. + +The encoded values will be integers. Depending on how many unique values you have, you can use ``TinyIntVector``, ``SmallIntVector``, ``IntVector``, or ``BigIntVector`` to hold them. You specify the type when you create your ``DictionaryEncoding`` instance. You might wonder where those integers come from: the dictionary vector is a regular vector, so the value's index position in that vector is used as its encoded value. + +Another critical attribute in ``DictionaryEncoding`` is the id. It's important to understand how the id is used, so we cover that later in this section. + +This result will be a new vector (for example, an ``IntVector``) that can act in place of the original vector (for example, a ``VarCharVector``). When you write the data in arrow format, it is both the new ``IntVector`` plus the dictionary that is written: you will need the dictionary later to retrieve the original values. + +.. code-block:: Java + + // 1. create a vector for the un-encoded data and populate it + VarCharVector unencoded = new VarCharVector("unencoded", allocator); + // now put some data in it before continuing + + // 2. create a vector to hold the dictionary and populate it + VarCharVector dictionaryVector = new VarCharVector("dictionary", allocator); + + // 3. create a dictionary object + Dictionary dictionary = new Dictionary(dictionaryVector, new DictionaryEncoding(1L, false, null)); + + // 4. create a dictionary encoder + DictionaryEncoder encoder = new DictionaryEncoder.encode(dictionary, allocator); + + // 5. encode the data + IntVector encoded = (IntVector) encoder.encode(unencoded); + + // 6. re-create an un-encoded version from the encoded vector + VarCharVector decoded = (VarCharVector) encoder.decode(encoded); + +One thing we haven't discussed is how to create the dictionary vector from the original un-encoded values. That is left to the library user since a custom method will likely be more efficient than a general utility. Since the dictionary vector is just a normal vector, you can populate its values with the standard APIs. + +Finally, you can package a number of dictionaries together, which is useful if you're working with a ``VectorSchemaRoot`` with several dictionary-encoded vectors. This is done using an object called a ``DictionaryProvider``. as shown in the example below. Note that we don't put the dictionary vectors in the same ``VectorSchemaRoot`` as the data vectors, as they will generally have fewer values. + + +.. code-block:: Java + + DictionaryProvider.MapDictionaryProvider provider = + new DictionaryProvider.MapDictionaryProvider(); + + provider.put(dictionary); + +The ``DictionaryProvider`` is simply a map of identifiers to ``Dictionary`` objects, where each identifier is a long value. In the above code you will see it as the first argument to the ``DictionaryEncoding`` constructor. + +This is where the ``DictionaryEncoding``'s 'id' attribute comes in. This value is used to connect dictionaries to instances of ``VectorSchemaRoot``, using a ``DictionaryProvider``. Here's how that works: + +* The ``VectorSchemaRoot`` has a ``Schema`` object containing a list of ``Field`` objects. +* The field has an attribute called 'dictionary', but it holds a ``DictionaryEncoding`` rather than a ``Dictionary`` +* As mentioned, the ``DictionaryProvider`` holds dictionaries indexed by a long value. This value is the id from your ``DictionaryEncoding``. +* To retrieve the dictionary for a vector in a ``VectorSchemaRoot``, you get the field associated with the vector, get its dictionary attribute, and use that object's id to look up the correct dictionary in the provider. + +.. code-block:: Java + + // create the encoded vector, the Dictionary and DictionaryProvider as discussed above + + // Create a VectorSchemaRoot with one encoded vector + VectorSchemaRoot vsr = new VectorSchemaRoot(List.of(encoded)); + + // now we want to decode our vector, so we retrieve its dictionary from the provider + Field f = vsr.getField(encoded.getName()); + DictionaryEncoding encoding = f.getDictionary(); + Dictionary dictionary = provider.lookup(encoding.getId()); + +As you can see, a ``DictionaryProvider`` is handy for managing the dictionaries associated with a ``VectorSchemaRoot``. More importantly, it helps package the dictionaries for a ``VectorSchemaRoot`` when it's written. The classes ``ArrowFileWriter`` and ``ArrowStreamWriter`` both accept an optional ``DictionaryProvider`` argument for that purpose. You can find example code for writing dictionaries in the documentation for (:doc:`ipc`). ``ArrowReader`` and its subclasses also implement the ``DictionaryProvider`` interface, so you can retrieve the actual dictionaries when reading a file. + +Slicing +======= + +Similar with C++ implementation, it is possible to make zero-copy slices of vectors to obtain a vector +referring to some logical sub-sequence of the data through :class:`TransferPair` + +.. code-block:: Java + + IntVector vector = new IntVector("intVector", allocator); + for (int i = 0; i < 10; i++) { + vector.setSafe(i, i); + } + vector.setValueCount(10); + + TransferPair tp = vector.getTransferPair(allocator); + tp.splitAndTransfer(0, 5); + IntVector sliced = (IntVector) tp.getTo(); + // In this case, the vector values are [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] and the sliceVector values are [0, 1, 2, 3, 4]. diff --git a/docs/source/vector_schema_root.rst b/docs/source/vector_schema_root.rst new file mode 100644 index 00000000..3119122d --- /dev/null +++ b/docs/source/vector_schema_root.rst @@ -0,0 +1,163 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. 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. + +============ +Tabular Data +============ + +While arrays (aka: :doc:`ValueVector <./vector>`) represent a one-dimensional sequence of +homogeneous values, data often comes in the form of two-dimensional sets of +heterogeneous data (such as database tables, CSV files...). Arrow provides +several abstractions to handle such data conveniently and efficiently. + +Fields +====== + +Fields are used to denote the particular columns of tabular data. +A field, i.e. an instance of `Field`_, holds together a field name, a data +type, and some optional key-value metadata. + +.. code-block:: Java + + // Create a column "document" of string type with metadata + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + + Map metadata = new HashMap<>(); + metadata.put("A", "Id card"); + metadata.put("B", "Passport"); + metadata.put("C", "Visa"); + Field document = new Field("document", new FieldType(true, new ArrowType.Utf8(), /*dictionary*/ null, metadata), /*children*/ null); + +Schemas +======= + +A `Schema`_ describes the overall structure consisting of any number of columns. It holds a sequence of fields together +with some optional schema-wide metadata (in addition to per-field metadata). + +.. code-block:: Java + + // Create a schema describing datasets with two columns: + // a int32 column "A" and a utf8-encoded string column "B" + import org.apache.arrow.vector.types.pojo.ArrowType; + import org.apache.arrow.vector.types.pojo.Field; + import org.apache.arrow.vector.types.pojo.FieldType; + import org.apache.arrow.vector.types.pojo.Schema; + import static java.util.Arrays.asList; + + Map metadata = new HashMap<>(); + metadata.put("K1", "V1"); + metadata.put("K2", "V2"); + Field a = new Field("A", FieldType.nullable(new ArrowType.Int(32, true)), null); + Field b = new Field("B", FieldType.nullable(new ArrowType.Utf8()), null); + Schema schema = new Schema(asList(a, b), metadata); + +VectorSchemaRoot +================ + +A `VectorSchemaRoot`_ is a container for batches of data. Batches flow through +VectorSchemaRoot as part of a pipeline. + +.. note:: + + VectorSchemaRoot is somewhat analogous to tables or record batches in the + other Arrow implementations in that they all are 2D datasets, but their + usage is different. + +The recommended usage is to create a single VectorSchemaRoot based on a known +schema and populate data over and over into that root in a stream of batches, +rather than creating a new instance each time (see `Flight`_ or +``ArrowFileWriter`` as examples). Thus at any one point, a VectorSchemaRoot may +have data or may have no data (say it was transferred downstream or not yet +populated). + +Here is an example of creating a VectorSchemaRoot: + +.. code-block:: Java + + BitVector bitVector = new BitVector("boolean", allocator); + VarCharVector varCharVector = new VarCharVector("varchar", allocator); + bitVector.allocateNew(); + varCharVector.allocateNew(); + for (int i = 0; i < 10; i++) { + bitVector.setSafe(i, i % 2 == 0 ? 0 : 1); + varCharVector.setSafe(i, ("test" + i).getBytes(StandardCharsets.UTF_8)); + } + bitVector.setValueCount(10); + varCharVector.setValueCount(10); + + List fields = Arrays.asList(bitVector.getField(), varCharVector.getField()); + List vectors = Arrays.asList(bitVector, varCharVector); + VectorSchemaRoot vectorSchemaRoot = new VectorSchemaRoot(fields, vectors); + +Data can be loaded into/unloaded from a VectorSchemaRoot via `VectorLoader`_ +and `VectorUnloader`_. They handle converting between VectorSchemaRoot and +`ArrowRecordBatch`_ (a representation of a RecordBatch +:external+arrow:ref:`IPC ` message). For example: + +.. code-block:: Java + + // create a VectorSchemaRoot root1 and convert its data into recordBatch + VectorSchemaRoot root1 = new VectorSchemaRoot(fields, vectors); + VectorUnloader unloader = new VectorUnloader(root1); + ArrowRecordBatch recordBatch = unloader.getRecordBatch(); + + // create a VectorSchemaRoot root2 and load the recordBatch + VectorSchemaRoot root2 = VectorSchemaRoot.create(root1.getSchema(), allocator); + VectorLoader loader = new VectorLoader(root2); + loader.load(recordBatch); + +A new VectorSchemaRoot can be sliced from an existing root without copying +data: + +.. code-block:: Java + + // 0 indicates start index (inclusive) and 5 indicated length (exclusive). + VectorSchemaRoot newRoot = vectorSchemaRoot.slice(0, 5); + +Table +===== + +A `Table`_ is an immutable tabular data structure, very similar to VectorSchemaRoot, in that it is also built on ValueVectors and schemas. Unlike VectorSchemaRoot, Table is not designed for batch processing. Here is a version of the example above, showing how to create a Table, rather than a VectorSchemaRoot: + +.. code-block:: Java + + BitVector bitVector = new BitVector("boolean", allocator); + VarCharVector varCharVector = new VarCharVector("varchar", allocator); + bitVector.allocateNew(); + varCharVector.allocateNew(); + for (int i = 0; i < 10; i++) { + bitVector.setSafe(i, i % 2 == 0 ? 0 : 1); + varCharVector.setSafe(i, ("test" + i).getBytes(StandardCharsets.UTF_8)); + } + bitVector.setValueCount(10); + varCharVector.setValueCount(10); + + List vectors = Arrays.asList(bitVector, varCharVector); + Table table = new Table(vectors); + +See the :doc:`table` documentation for more information. + +.. _`ArrowRecordBatch`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/ipc/message/ArrowRecordBatch.html +.. _`Field`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/types/pojo/Field.html +.. _`Flight`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/flight/package-summary.html +.. _`Schema`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/types/pojo/Schema.html +.. _`Table`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/table/Table.html +.. _`VectorLoader`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/VectorLoader.html +.. _`VectorSchemaRoot`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/VectorSchemaRoot.html +.. _`VectorUnloader`: https://arrow.apache.org/docs/java/reference/org/apache/arrow/vector/VectorUnloader.html