You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by li...@apache.org on 2021/12/15 19:38:12 UTC

[arrow] 02/02: ARROW-14421: [C++] Implement Flight SQL

This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch flight-sql
in repository https://gitbox.apache.org/repos/asf/arrow.git

commit 9c2d8ed5e82a5dd33ca0e17c71c7ca4a3f72b855
Author: Rafael Telles <ra...@telles.dev>
AuthorDate: Wed Dec 15 14:33:04 2021 -0500

    ARROW-14421: [C++] Implement Flight SQL
    
    Squashed commit of the following:
    
    commit 72ce72ba855909052f7dfb898105b419697157c8
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Dec 6 16:55:20 2021 -0300
    
        Fix documentation for GetSqlInfo on FlightSql.proto
    
    commit 076187ec3aa18295c92de1f38b9036e66fa8ca7e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Dec 6 15:02:45 2021 -0300
    
        Add better description to table types on FlightSql.proto
    
    commit 9a9b536acf207456c8050d165c4f1a12c7d71010
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Dec 6 15:01:21 2021 -0300
    
        Change SQL_NUMERIC_FUNCTIONS result on sqlite_sql_info to uppercase
    
    commit dd9d507997e1bcf88aeb3511889dcb3f6b777283
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Dec 6 15:00:19 2021 -0300
    
        Remove dependency on boost/lexical_cast.hpp
    
    commit 023f71a12fbd233dbba2571c7935db454516293e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 16:47:34 2021 -0300
    
        Use std::generate_n to generate random string on sqlite_server.cc
    
    commit 6a928ca82ae69e579d4785c092d069f6b2439ceb
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 17:09:25 2021 -0300
    
        Move implementation of methods and data from SQLiteFlightSqlServer to SQLiteFlightSqlServer::Impl and remove dependency of boost-uuid (#221)
    
        * Use pimpl idiom on sqlite_server and add comments on protobuf file
    
        * Correctly implement impl pattern
    
    commit cfe9e2ac79412d375e1415f40445589fd33500d6
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 16:27:46 2021 -0300
    
        Use EXPECT_RAISES_WITH_MESSAGE_THAT on TestFlightSqlServer#TestCommandGetSqlInfoNoInfo
    
    commit de8600ca83046bdb51b1a6e1c6420e36896d7e3d
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 13:49:55 2021 -0300
    
        nit: Fix indentation on cpp_build.sh
    
    commit ea94097953448a48ebd31215113859f00d06dc4a
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 12:15:44 2021 -0300
    
        Use TCP instead on unix sockets on server_test.cc
    
    commit 99ae0216466aee26944b31e10d46d2c3f372842d
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Dec 3 12:03:45 2021 -0300
    
        Remove need of RunServerInternal
    
    commit 2b3839aff39b474d77d515e0ce6290e8e1a9f81a
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Dec 2 13:48:49 2021 -0300
    
        Remove generated protobuf enum from example application
    
    commit 8431e41bddda5abc8aeec8a363b40deab352e5a4
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Dec 2 13:35:56 2021 -0300
    
        [C++] Address ratification comments (round 4 - part 4) (#215)
    
        * Make other methods from SQLite server example to return arrow::Result instead of Status
    
        * Fix bug for null values in numeric columns on SQLite server example
    
        * Structure catalog-schema-table tuple on a TableRef struct on client
    
        * Rename 'schema' to 'db_schema'
    
        * Use TableRef struct on server.cc
    
        * Undo renaming db_schema_filter_pattern
    
    commit fe9d7dc5a1b26a00c789cee969e750479353f581
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Dec 2 13:28:23 2021 -0300
    
        Make sure to wait for server to be ready before running tests (#220)
    
        * Make sure to wait for server to be ready before running tests
    
        * Start server independently for each test
    
        * Use unique_ptr for server thread on server_test.cc
    
    commit e8d8a13aa82c0ec929a103dcc81f250ab0dda02b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Nov 26 13:59:04 2021 -0300
    
        [C++] Address ratification comments (round 4 - part 3) (#214)
    
        * Make other methods from SQLite server example to return arrow::Result instead of Status
    
        * Fix bug for null values in numeric columns on SQLite server example
    
        * Add comment regarding to performance on sqlite_statement_batch_reader
    
        * Separate GetSqlInfoResultMap from sqlite_server.h
    
        * Remove unused parameter on DoPutCommandStatementUpdate
    
    commit c36b81706a9d6260a733b5ad48baa26694759ddc
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Nov 26 13:25:38 2021 -0300
    
        Remove PRECOMPILED_HEADERS option on arrow_flight_sql's CMakeList.txt
    
    commit 9e3c928cd870c81f1e2f4e2210c1fb3b05a4182e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Nov 25 16:24:16 2021 -0300
    
        [C++] Address ratification comments (round 4 - part 2) (#213)
    
        * Make FlightSqlClient::GetFlightInfo return a Result instead of Status
    
        * Make most methods on FlightSqlServerBase to return a Result instead of Status
    
        * Move private functions on server.cc to anonymous namespace
    
        * Fixes on doxygen and better readability on server_test.cc
    
        * Rename fields on client_test.cc to follow the convention
    
    commit 7d74b7efce86f77fd1b42716cec4d6b7c3a3bd13
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Nov 25 11:15:37 2021 -0300
    
        [C++] Address ratification comments (round 4) (#212)
    
        * Fix minor issues on sql_info_internal
    
        * Change table_types parameter to be a pointer
    
        * Improve GetSqlInfo error in case of no info
    
        * Replace ArrayFromVector to ArrayFromJSON in most cases
    
        * Improve server_test assertions and code quality
    
    commit 56d84e9fd210a74c0aea7fb4675b76e35e12de76
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Nov 19 14:27:50 2021 -0300
    
        Rename directory flight_sql to sql (#210)
    
    commit 9fcacf22c236ce45fd31ce13b0c4cf1370dee877
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Nov 15 18:13:57 2021 -0300
    
        Fix CMake minor issue, add sql_info_types.h
    
    commit 76d04ea0dff846a1a3f5bf0176af1e287f4c05d3
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Nov 15 17:36:24 2021 -0300
    
        [C++] Fix comments on ratification PR (round 3) (#205)
    
        * Fix comments pointed on review
    
        * Replace boost::variant to arrow::util::variant
    
        * Remove unused macros and redundant definitions
    
        * Refactor sql_info_internal to prepopulate builder pointers on constructor
    
        * Replace ArrayFromJSON usage to ArrayFromVector for consistency on tests
    
        * Remove mention to GetFlightInfoForCommand from doxygen
    
        * Remove copy constructors on SQLInfo related visitors
    
        * Remove move constructors on SQLInfo related visitors
    
    commit fd9bd948002ec393b05a1dfe50f4c8dceb5e4635
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Nov 15 11:25:53 2021 -0300
    
        Fix build when BUILD_EXAMPLES or BUILD_TESTS is OFF
    
    commit f0555708f3b386382b1f32e28c825dce9802171b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Nov 12 13:03:49 2021 -0300
    
        Enable Flight SQL C++ on CI
    
    commit 6c98d52656971a00cd863c40ca87708a6c1e4488
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 11 16:38:45 2021 -0300
    
        Fix FindSQLite3Alt.cmake
    
    commit 3cee40c5ddf7fc066e2d473202576284a946e66b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Nov 11 14:56:58 2021 -0300
    
        Fix code style issues
    
    commit ec1c4d0e2b36ef51a18cd28fdad6783bd3d69430
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Nov 11 13:21:25 2021 -0300
    
        Fix ODR violation when linking protobuf
    
    commit 2e48187c2c683ea82f79d1abef520b385ed66b69
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 11 13:53:06 2021 -0300
    
        Fix method docs on server.h
    
    commit c778682eee06d1dd6bb744ce979b165b8c1b8bc4
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Nov 11 08:50:21 2021 -0300
    
        Change Status to arrow::Result on CreateStatementQueryTicket method
    
    commit 45b58cb00a48a751b231ecd054fe48e6e8741767
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Nov 10 17:31:05 2021 -0300
    
        Add documentation on public method GetFlightInfo
    
    commit 6908d3851b1c85a9db15bb58c20ab818d7a98ef2
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Nov 10 17:21:02 2021 -0300
    
        Change status type on sqlite classes
    
    commit 26f0c194fce55951d1c4af96e69b3cd0862e046d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Nov 10 17:20:35 2021 -0300
    
        Make parameter ordering consistent
    
    commit 8ececc8b17431fc50b40d2a045f86adc1d41974b
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Nov 10 17:19:21 2021 -0300
    
        Remove old use of CreateStatementQueryTicket
    
    commit 9e352e3a77231942562964c968926d32e3d4cea2
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Nov 10 17:18:48 2021 -0300
    
        Change CreateStatementQueryTicket to a free function
    
    commit 776d739476793cd47f70f156e144f966ec338126
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 11 11:25:45 2021 -0300
    
        Fix method docs errors on server.h and sqlite example
    
    commit 404b27deb0df2833175a58efe63eaf718e50ffd1
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 10 17:29:01 2021 -0300
    
        [C++] Implement GetSqlInfo on server example (#193)
    
        * WIP: Implement GetSqlInfo on server-side
    
        * Fix build and missing code parts
    
        * Add test for map<int, list<int>> @ GetSqlInfo
    
        * Fix integration tests
    
        * Fix comments pointed on review
    
        * Fix comments pointed on review
    
        * Add more comments about the logic around DenseUnionArrays
    
        * Remove unnecessary includes on sqlite_server.cc
    
        * Fix comments pointed on review
    
        * Use std::vector reserve and assign to avoid allocating temporary object
    
        * Remove unused dependencies on server.cc
    
        Co-authored-by: Abner Eduardo Ferreira <ab...@pm.me>
    
    commit 817baf9a5d347fc5d8cbf9c598458fbb85acfc3a
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 10 15:05:20 2021 -0300
    
        Fix code style issues
    
    commit 93f93c4c1148b394b660461f20a8a7511ff23546
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Wed Nov 10 14:27:18 2021 -0300
    
        [CPP] Fix issues from client files in flight-sql (#201)
    
        * Order include and rename protocol namespace
    
        * Change a reference to pointer in the client client and refactor the name of namespace on functions
    
        * Nit: remove empty line
    
        * Improve naming from protocol::sql namespace
    
        * Fix include and its orderding
    
    commit 0519976bc9299607444c0f21141b939da6142da0
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 10 14:23:05 2021 -0300
    
        Update vcpkg.json and remove unused variable on CMakeLists.txt (#200)
    
    commit df1ce715fe6382979ceaa11cfc6e66d5219a5562
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Wed Nov 10 13:32:53 2021 -0300
    
        [CPP] Modify the way arrays are created in tests (#199)
    
        * Create array using method ArrayFromJson
    
        * Create array using method ArrayFromJson in server tests
    
        * improve conditional from if statement
    
    commit 35a530ae5a8e0ca2dd5374bbddc94ebe10aef745
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 10 12:13:15 2021 -0300
    
        Fix FindSQLite3Alt.cmake
    
    commit 5e1c200bffcec0e7f3a4038177233f5df9e46957
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 10 08:38:32 2021 -0300
    
        Fix linter erros on FindSQLite3Alt.cmake
    
    commit 5f5ea06da8b7c62ea9419add7750f37fdedbf87f
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 10 08:37:05 2021 -0300
    
        Fix wrong parameter name on SetParameters docs on client.h
    
    commit d40ef9994e532538615d4ca16a908db1dd8df631
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Nov 9 17:57:42 2021 -0300
    
        Reestructure FlightSqlClient to use virtual methods (#195)
    
    commit 093b539df8f16e6e319869b7acbdae7789e91e47
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Tue Nov 9 16:03:32 2021 -0300
    
        Fix protobuf version on flight-core
    
    commit 71f877df2e34b10e5c83e3540aca164ec0d46873
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Mon Nov 8 16:13:34 2021 -0300
    
        Remove wrong line on CmakeLists
    
    commit 0b32b272ac1bd9b16d1bd4a7d2521db9780ffc30
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Mon Nov 8 15:03:58 2021 -0300
    
        implement FindSQLite3Alt on cmake modules
    
    commit e2e57ae39876e1978138ca2b3d66ea9ecacaf7fc
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 4 13:04:00 2021 -0300
    
        Fix sql_client close before server stop server test
    
    commit f604d468f9ba885a51f738c93c055d8f8d064f19
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 4 13:00:14 2021 -0300
    
        Fix sql_cliente close before server on server test
    
    commit 9135703c25e4f54bd3d487676ce62739fbb3e52e
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 4 12:41:09 2021 -0300
    
        server.reset() and sql_client.reset() on server test TearDown
    
    commit 2483ff4cfd7674a2ad5f6a432569419bcbf179d9
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Nov 4 11:49:11 2021 -0300
    
        Change sql_client to unique_pointer on server test
    
    commit 46569759a234cf62a1897bdd2ae12687faba852c
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 3 18:23:23 2021 -0300
    
        Change server to unique pointer on server tests
    
    commit 698645d127de4647e72cc93236f4700fc0a7a6ea
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 3 17:26:24 2021 -0300
    
        fix server stop on serve rt
    
    commit 70bda0c5c117121787922987b6687749c73a2ea0
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 3 16:01:06 2021 -0300
    
        Fix test_server
    
    commit 24343e24df274eb1566971ab139f7ac33dc5fb6a
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 3 15:42:56 2021 -0300
    
        Fix memory leak with mock
    
    commit 89b4f17cf9aac989f9aa09fa6a0d2113532d9a9d
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Nov 3 14:04:58 2021 -0300
    
        fix: correct issues on tests
    
    commit 996c708e6835adad8468d5ece6ba5e5ca2693d4b
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Tue Nov 2 16:28:08 2021 -0300
    
        Format test files
    
    commit d480e77dd7c54a18823f2af3f676755d8f4e8785
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Oct 28 14:55:12 2021 -0300
    
        Made fixture to client_test.cc to avoid duplication
    
    commit 75f67ad5e2cefde1cd0d6145a928066b6a9b5215
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Oct 27 11:02:19 2021 -0300
    
        Fix some issues on CMake and Server Test
    
    commit 4abcd45a0e0706c9d8da1cbb79807971acbb0bf1
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Tue Nov 2 15:18:00 2021 -0300
    
        fix: merge test client
    
    commit 39f63c24e41a8f9d83793696e38435dc83890e87
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Thu Oct 28 14:55:12 2021 -0300
    
        Made fixture to client_test.cc to avoid duplication
    
    commit 9ef1cae7645fc4456d55d6bd8e365368b69b8533
    Author: Juscelino Junior <ju...@id.uff.br>
    Date:   Wed Oct 27 11:02:19 2021 -0300
    
        Fix some issues on CMake and Server Test
    
    commit 697cde3dab946f727d56d7a0cbdec68ce7b9a301
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 3 17:54:35 2021 -0300
    
        [C++] Ensure client_test.cc does not violate ODR (#192)
    
        * Ensure client_test.cc does not violate ODR
    
        * Format CMakeLists.txt
    
    commit 9716a20c949717eeb3db064df9ee15184d0bea2d
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 3 16:12:07 2021 -0300
    
        Fix issues reported by cppcheck
    
    commit 92c546444714402dbf7e25f6643c08951e98c800
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Nov 3 13:17:52 2021 -0300
    
        Remove unused variable on CMakeLists.txt
    
    commit f080caa3739a71992bf11ec37c03485eb604f763
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Tue Nov 2 17:42:37 2021 -0300
    
        [CPP] Fix on sqlite classes from FlightSqlServer (#181)
    
        * Change FlightMetadataWriter and FlightMessageReader from unique_ptr to raw ptr
    
        * Change reinterpret_cast to dynamic_cast
    
        * Add const to sqlite3Stmt getter
    
        * Add const to string parameter
    
        * Using ARROW_ASSIGN_OR_RAISE to buffer and batch creation
    
        * Change from unique_ptr to raw ptr
    
        * Change sqlite_tables_schema_batch_reader extension from cpp to cc
    
        * Avoid instantiate a string object
    
        * Make ExecuteSql return Status
    
        * Change Create methods from sqlStatement to return Result<T>
    
        * Fix from rebase
    
        * Fix from rebase
    
        * Fix server initialization
    
        * Fix checkstyle
    
        * Add missing ;
    
        * Fix docs on methods
    
        * add explicit to sqlite_server.h constructor
    
        * Fix double free
    
        * Add comment of ownership to SQLiteFlightSqlServer constructor
    
        * Fixed possible close with null_ptr on sqlite classes
    
        * Fix style issues
    
        * Use static_cast instead of dynamic_cast
    
        * Fix other review comments
    
        * Use 'static_cast' when casting scalars on sqlite_server.cc
    
        * Fix comments pointed on review
    
        Co-authored-by: Rafael Telles <ra...@telles.dev>
    
    commit c079a477bb849594ff1e899dab4983519bed8d90
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Nov 2 17:30:40 2021 -0300
    
        [C++] Use util::optional on Flight SQL structs (#191)
    
        * Use optionals on Flight SQL command structs
    
        * Use delete instead of free() on server_test.cc
    
        * Un-nest PreparedStatement class from FlightSqlClient
    
        * Make PreparedStatement::IsClosed const
    
    commit b26bfc40d4a85d0a685d7a985e1541828f613027
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Nov 2 14:54:53 2021 -0300
    
        [C++] Other fixes for ratification (#190)
    
        * Rename sqlClient to sql_client on test_app_cli.cc
    
        * Add missing parameters on PreparedStatement constructor
    
        * Add NOTE to PreparedStatement destructor
    
        * Parse PreparedStatement's dataset and parameters schema when constructing
    
        * Rename Flight SQL Actions constants
    
        * Remove unnecessary 'using' keyword on server.h
    
        * Clean up header files and includes
    
        * Rename getters for schemas on PreparedStatement and make them const
    
        * Handle possible protobuf parsing errors on server.cc
    
        * Move CreateStatementQueryTicket implementation to sql namespace
    
    commit 8095b6e9b8593fb15d528c863f9d9a8963b02013
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Nov 1 14:04:48 2021 -0300
    
        Remove unused includes on server.h
    
    commit 166bb6b40544ea0a58354b1e553711338ceddcea
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Nov 1 14:02:01 2021 -0300
    
        Rename directory flight-sql to flight_sql
    
    commit d89a82ee66a67e22e88dca5a766a3c0e57d9c1f1
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 28 17:57:08 2021 -0300
    
        Remove unnecessary const modifiers on client interface
    
    commit 838469fae0a8cab867d3574a74d0ffccd6a5faa4
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 28 17:23:04 2021 -0300
    
        Remove explicit using of GFlags namespaces
    
    commit 3d0dcfecb203fe89354a0422592873a17bc8ca92
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 28 17:07:26 2021 -0300
    
        [C++] Remove templating from client interface (#184)
    
        * Fix variable initialization
    
        * Remove templating from FlightSqlClient and PreparedStatement classes
    
        * Fix inconsistent member names
    
        * Make PreparedStatement an inner class of FlightSqlClient
    
        * WIP: Use references on FlightCLientImpl methods
    
        * Use shared_ptr<FlightClientImpl> to avoid dangling pointers
    
        * Log error when deleting PreparedStatement
    
        Co-authored-by: Jose Almeida <al...@gmail.com>
    
    commit f1199debe277b0629692d27d0a409b016b6107df
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Thu Oct 28 16:56:28 2021 -0300
    
        Add ARROW_EXPORT and change designated initializer from struct (#189)
    
    commit 4dfa02606adbea3947696024d9fc50a6336bbce9
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Oct 27 13:17:40 2021 -0300
    
        Handle errors on all Parse and Unpack calls to Protobuf (#185)
    
    commit 92b7b48a346f46d4f0049ea6479a29609ade7399
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 26 17:52:14 2021 -0300
    
        Improve DoPutUpdateResult parsing
    
    commit 6ab6e27e2262a5bc535e075747693e97740ee190
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 26 13:49:46 2021 -0300
    
        Change ASSERT_TRUE to AsserTableEqual()
    
    commit 30929b7920d477c2a9f2d7d69dc5d9253c4349f2
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 26 11:55:35 2021 -0300
    
        Fix linting issues
    
    commit 5acfa2325674dd61a31e6146e5e29289d1ffbd72
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 26 11:29:48 2021 -0300
    
        Fix compiler warnings on client_impl.h
    
    commit fdbe5f1a6368d3232bc62fea0cd4663e61e67cf8
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 26 11:19:41 2021 -0300
    
        [C++] Wrap Protobufs on server (#182)
    
        * WIP: Wrap CommandStatementQuery protobuf into a struct
    
        * Wrap all Protobuf for commands on server
    
        * Change Parse methods as anonymous functions
    
    commit 5010c1baac182dc7b318e67a6c91da3d4cf66f79
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Oct 25 15:42:35 2021 -0300
    
        Fix flaky tests
    
    commit ba7332757b197a34c2b3619690419816f0c62820
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Oct 25 15:00:08 2021 -0300
    
        [C++] Improvements on CMakeLists.txt (#178)
    
        * Remove extra options on CMakeLists.txt
    
        * Remove GRPC-related instructions from CMakeLists.txt and guard SQLite requirement on ARROW_BUILD_TESTS
    
        * Remove redudant dependencies on CMakeLists.txt
    
    commit 9a96bbdcd1befd5c93aa9bbd47dcd4a78c070666
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Mon Oct 25 14:23:50 2021 -0300
    
        [CPP] Change the way arrays are created in flight-sql tests (#179)
    
        * Create arrays using methods MakeArrayOfNull and ArrayFromJSON
    
        * Fix checkstyle
    
        * Remove macro declare binary array
    
    commit 3895b53c5a49b37ebc403c7aa4f897eb06a85209
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Oct 25 11:26:31 2021 -0300
    
        [C++] Improve Client interface (#180)
    
        * Use '#pragma once' on header files
    
        * Remove ghost parameter on Doxygen for PreparedStatement.Execute
    
        * Change client interface to use Result instead of Status
    
        * Rename sqlClient to sql_client on tests
    
    commit 655d8dd0b88ccfc0905226dc6fa1058c9917f22c
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 22 16:26:19 2021 -0300
    
        Use ASSERT_OK on client_test.cc
    
    commit 87e02ac0921ff4277adf8dae10347f222c9b0c37
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 21 14:06:38 2021 -0300
    
        Fix linter issues
    
    commit 8b5324f730ca55cc556b3e9517ca40af7826dac6
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 19 11:18:17 2021 -0300
    
        Rename flight-sql/server.cpp to server.cc
    
    commit e9aafe7c3e9dafa348bf9efea9bc8e2dfd3d091f
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 19 11:09:31 2021 -0300
    
        [C++] CommandGetCrossReference (#172)
    
        * Add CommandGetCrossReference on FlightSql.proto
    
        * Implement CommandGetCrossReference on C++ client
    
        * Implement CommandGetCrossReference on C++ server example
    
        * Update FlightSql.proto
    
        * Update FlightSql.proto
    
    commit 84ae269e1f131e175bc254cb7bef408b957870a7
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Oct 18 16:10:10 2021 -0300
    
        Implement CommandPreparedStatementUpdate on SQL server example (#169)
    
        * Implement CommandPreparedStatementUpdate on SQL server example
    
        * Refactor sqlite_server to avoid duplication
    
    commit d0e94764bbfc0dc0f6485b122b875f6de1d6e58c
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Oct 18 14:21:42 2021 -0300
    
        Fix wrong error messages on server.cpp
    
    commit 6248009942d05bd955c1f6e4ef4a67c8ac3311bb
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 14 17:59:19 2021 -0300
    
        Rename server files
    
    commit a89a8ffec3a0080cc0ab28fe78599c50afc6c782
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 14 17:28:38 2021 -0300
    
        Fix CheckStyle issues
    
    commit b30041539f491f31df46abd94e121cd870931905
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Thu Oct 14 14:15:38 2021 -0300
    
        Fix resource leak where record batch is being created for client_impl.h
    
    commit 5d03029a092075b7ff816cf32f995f864aaf4bb0
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Wed Oct 13 17:57:26 2021 -0300
    
        Minor refactor: move variables closer to used
    
    commit ade534d655d4ad99da920db891015774a034f2df
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Wed Oct 13 16:04:20 2021 -0300
    
        Fix SIGSEV in test case for PreparedStatement.ExecuteUpdate with parameter binding
    
    commit b9205c1de5b11e89c3c3bee5d24305f2ba8bd4ad
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 13 15:32:41 2021 -0300
    
        Refactor ExecuteUpdate with parameter binding test
    
    commit 14d2725e337e1d297eeb6638742f94c5bf560728
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Wed Oct 13 14:46:46 2021 -0300
    
        WIP: Refactor test cases for PreparedStatement.ExecuteUpdate to use lambda functions
    
    commit 464248d4ef4fa8b9f17be8b96f5671f0f9eec2a6
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Wed Oct 13 12:05:25 2021 -0300
    
        WIP: Refactor test cases for PreparedStatement.ExecuteUpdate
    
    commit 86fe0463d75db9059b4a73a3d71a4a17ccf05905
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Tue Oct 12 17:33:48 2021 -0300
    
        Fix rebase issues
    
    commit d9ab9348e49ed2aef29183df0f0315693cc6b676
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Tue Oct 12 17:19:46 2021 -0300
    
        Add test case for PreparedStatement.ExecuteUpdate with parameter binding
    
    commit 2d23d07476b9be306507a00b76ffd8eae5053b7b
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Tue Oct 12 16:36:22 2021 -0300
    
        Fix broken test for PreparedStatement.ExecuteUpdate without parameter binding
    
    commit 22320fd20ecf83e92406df0dd3c3d97d7f465895
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 18:04:35 2021 -0300
    
        Make changes regarding to reviews
    
    commit 247be5bc7b0fdef71d4ce16747fc1185b59bb245
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 18:04:35 2021 -0300
    
        Make changes regarding to reviews
    
    commit a9c11db01101843e96e8c88468959bd9b8e21e4f
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 15:07:01 2021 -0300
    
        Add integration tests for PreparedStatement query
    
    commit 54ecc387d745b38b66f1bd6a1d0d96cc05cd9ffa
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 14:04:05 2021 -0300
    
        Add missing docs for GetArrowType
    
    commit b8417b1a2295730772de68975b7a82c0e1bb655d
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 13:55:19 2021 -0300
    
        Remove GetArrowType method duplicate
    
    commit 66a0e41244bc3aa2df7a935dfcd2cc735d3bb138
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 7 13:35:55 2021 -0300
    
        Implement parameter binding on Flight SQL server and example
    
    commit 1bf1ea0dd737c89be10646be2a99b6bd08c3106f
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 5 15:15:03 2021 -0300
    
        WIP: Implement prepared statement on server example
    
    commit 1e437aa119dd8d807918ba46b7b8ec8076142a35
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 5 14:00:35 2021 -0300
    
        Add Create/Close prepared statement actions to sql_server
    
    commit 6e2c9e9bf84ccad95cf700a3ac4b94d7636039b7
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 12 15:27:09 2021 -0300
    
        [C++] Implement CommandGetImportedKeys and CommandGetExportedKeys (#163)
    
        * Implement CommandGetImportedKeys and CommandGetExportedKeys on Flight SQL Server example
    
        * Refactor DoGet methods to reduce code duplication
    
    commit 171d540ac82c3b4bfbb0a5ab53f9d9362dc7c67e
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Tue Oct 12 14:31:14 2021 -0300
    
        [CPP] Implements GetPrimaryKeys on flight sql server (#162)
    
        * Add Schema template for the primary keys
    
        * Implement GetPrimaryKeys on server
    
        * Add an integrated test for GetPrimaryKeys
    
        * Fix checkstyle
    
        * Add a comment to the query on primary keys query
    
        * Use GetFlightInfoForCommand helper method on GetFlightInfoPrimaryKeys
    
        Co-authored-by: Rafael Telles <ra...@telles.dev>
    
    commit e8efe631e106c2dc9788af6db99899353a7b10d4
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Oct 8 15:57:04 2021 -0300
    
        Implement CommandGetTableTypes on server example
    
    commit 042fcc250c597bb03e95901b739251b68ff04557
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 15:17:46 2021 -0300
    
        use variable close to its use
    
    commit afd0e3e8c6af317d23dbfb0ad64b9ee8c1aeaf21
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 15:17:00 2021 -0300
    
        Add comment when calling ReadMetadata on DoPut
    
    commit 51462791dcb003b4b2e56795403577e646ef78b7
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 15:16:28 2021 -0300
    
        Sort includes alphabetically
    
    commit 2f1c93585632e62e6baf73c6f0d5f977015eca31
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 15:15:54 2021 -0300
    
        change const references from setParameters method
    
    commit 8327b47cb50c3808e0ed06fc63ac99419efc3a35
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 14:21:43 2021 -0300
    
        change (void) to ASSERT_OK on test
    
    commit 432b0267573138134219e78d22fc9aea4e4c92f6
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 14:21:26 2021 -0300
    
        Implement method GetResultSetSchema
    
    commit eb2f76ce63cbbe514288886f9c8ab5c0157971c9
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 13:39:56 2021 -0300
    
        Add missing docs to prepared statement methods
    
    commit e3facf744f7ad0d4466a28121a522a8542020387
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 13:27:35 2021 -0300
    
        Add prepared statement parameter binding to test app
    
    commit b59afc612594fdcfa64f1d633c263856f044d25b
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 13:27:11 2021 -0300
    
        Create a mocked client test for parameter binding
    
    commit 15a6b3d17ce168a30c83049185bd9a97c7ad5311
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 13:26:00 2021 -0300
    
        Pass option to DoPut CALL
    
    commit b19552ee18036912f21994f20349fb831206b345
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 8 13:25:35 2021 -0300
    
        Fix add a break line
    
    commit b2b1b8c3c854048ef793160e69a8f098ab4133a0
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:50:34 2021 -0300
    
        Add macro ARRROW_RETURN_NOT_OK
    
    commit 9ccb72e90d2bee8d77b704156f99f2785665191e
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:49:28 2021 -0300
    
        remove TODO
    
    commit 7f9daa9f467461c3b4010417588131c05b8d8fa8
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:49:00 2021 -0300
    
        remove unused code from test_app
    
    commit 832aec33f9b3667e7d60681a1acd70f3bcdb26d6
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:29:57 2021 -0300
    
        add paremter binding to query execution with prepared statement
    
    commit e98fd67350e7c8839a1bdc361cb55c07a026e501
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:29:25 2021 -0300
    
        Add methods to set and get parameters
    
    commit e85e8f84e04a1e6fb60672ae352481fd45a8a3fc
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:39:18 2021 -0300
    
        Fix checkstyle
    
    commit 46c568171b8d0f45a19b6929e5fac7f6d605e64b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 5 13:14:55 2021 -0300
    
        Implement CommandGetSchemas
    
    commit ca738150306bab8866f4398749d697fdfddf7000
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 7 17:49:05 2021 -0300
    
        Add missing docs for DoPutCommandStatementUpdate
    
    commit 08b029434dcae677e27f53fe4cc1d7fec02f0b7e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 7 14:20:27 2021 -0300
    
        Implement CommandStatementUpdate on server example
    
    commit ac77dbc9b310271b8ec578d96299e8b05dc4cb0b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 7 17:52:07 2021 -0300
    
        Change SqliteStatement.Reset argumento to pointer
    
    commit dcc8f7fb5f80d69abe78334dd0eb0ad770411772
    Author: Abner Eduardo Ferreira <ab...@protonmail.ch>
    Date:   Thu Oct 7 15:37:01 2021 -0300
    
        Add GetSqlInfo on client side for FlightSQL
    
    commit f8f404ada1c5fd778a60912597c61f5f1a0d8bd4
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Oct 7 15:06:11 2021 -0300
    
        Remove duplicate import
    
    commit 3c3b928fd22ad1b35421eb7108dfa336f5bc3073
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:44:12 2021 -0300
    
        Make the mock object be called twice
    
    commit a9361aa41958be9494da639a6bc495b31bf4120f
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:16:18 2021 -0300
    
        Remove options as parameter from PreparedStatement methods
    
    commit 6759818b9567692dad9e6166a897b06041f98b66
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 14:04:10 2021 -0300
    
        Make the destructor call the Close method
    
    commit 8f23bd5953697a7559d9e8818f91ff65c9cd4969
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:50:24 2021 -0300
    
        rename the variable of the CommandPreparedStatementQuery
    
    commit d30cef29e2a51c2bc4d476471b0e7740dd029098
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:49:51 2021 -0300
    
        Move constructor to the top of the file
    
    commit de161ec9d10eb1a8cf563e4b33cce247f24fd764
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:49:17 2021 -0300
    
        Remove extra lines and duplicated import
    
    commit 8d60000190848035b3d138a3f92ea6ad331d2b3d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 16:42:19 2021 -0300
    
        Remove duplicate function due to rebase
    
    commit 78b454094835bff1a829f3dec1e3cbb4a5d0fcf3
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 16:28:22 2021 -0300
    
        Modify the constructor of the PreparedStatement
    
    commit f80bfafaa4b93513010b0fe4651720a87f6f8a25
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 16:28:10 2021 -0300
    
        Pass options to the client calls
    
    commit 4b0adb6cabf9f357ca7afe53ea64d5b8a0aa262d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 15:13:30 2021 -0300
    
        Fix test_app for prepared statement execution
    
    commit 297b7f06968e859149b6adca4aa4d4f7d5e2f637
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 15:09:04 2021 -0300
    
        Treat if the statement is already closed
    
    commit a46dd6db945744c0b7f7ae81cf4abc6a07475f2d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 14:59:15 2021 -0300
    
        Adding missing files from rebase
    
    commit 9414a2ef4a054ced35ed7c49dd9963db879dfa01
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 14:59:05 2021 -0300
    
        Fix checkstyle
    
    commit a5c362c2f59929f3fe4077c87f882a3f1b09ebca
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 22:31:36 2021 -0300
    
        Add a branch on test_app to execute a query with PreparedStatement mode
    
    commit fe96fe096dbba6088808dee434f262aeda5d304b
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 22:31:09 2021 -0300
    
        Refactor the creation of the PreparedStatement on sql_client
    
    commit ea75e69ba05b25b5aaa9752a91cd046edd1135f7
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 22:18:31 2021 -0300
    
        Create a mocked sql_client test for the preparedStatement
    
    commit ee1ed1930d8f3374760388f7ac394f6197174629
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 22:18:12 2021 -0300
    
        implemenet methods PreparedStatemenetClass in sql_client
    
    commit 83b1c169d6604b346d373509e301cb10f56aec49
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 22:16:37 2021 -0300
    
        Add PreparedStatementClass to sql client
    
    commit 3b08157e1ab88515e4f67c966868ccd31a4ac033
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 29 17:16:52 2021 -0300
    
        Implement CommandGetSchemas
    
    commit 3066113d217e508d409c27c8eb626682057cd629
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:39:18 2021 -0300
    
        Fix checkstyle
    
    commit 70800d0a19d046aefd6b4ced477a9a339846e81d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 28 16:09:27 2021 -0300
    
        Add the schemas to be used by the GetTabkes
    
    commit 2a221a6a957c479253fa820c792fec6e573df2c6
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 5 13:14:55 2021 -0300
    
        Implement CommandGetSchemas
    
    commit 1036ec8c11f1a7992f7a435762702dbc199152c7
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:39:21 2021 -0300
    
        Remove extra line
    
    commit a7ff4573a72ffed2eb401ae291b29dc0b3477b12
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:39:09 2021 -0300
    
        Add missing return at docs from SqlSchema methods
    
    commit 6c52c281353322d9416e10d2d393bbfe46ffbcfd
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:35:22 2021 -0300
    
        Passes the rc variable as reference to Reset method
    
    commit b7089548f4219cbb8e994d24da427a0bd060aa00
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Oct 7 13:32:12 2021 -0300
    
        Remove unused RecordBatchReader
    
    commit 250a3c4b4a3db0967c17bb8384c8e3ad7976d598
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 16:11:18 2021 -0300
    
        Add missing include
    
    commit 95e7507622be9037b819a17ed7f1163dad1893a1
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 16:00:20 2021 -0300
    
        Fix missing files from rebase
    
    commit acfaec0df1c08c32907cb05093fd2b23ddf2dbcb
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 14:15:41 2021 -0300
    
        Add a variable to control the execution flow
    
    commit d10d1916c3cf7777029b4803f2d86045799a6da6
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 14:15:19 2021 -0300
    
        Add a method to reset the statement to its original condition
    
    commit ec0319c26d2c44e7f30855066e3212064e7f4d03
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 13:25:19 2021 -0300
    
        Fix checkstyle
    
    commit 6bf3fdecf5f55e0715a354c09b89ec5407e14624
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 12:00:26 2021 -0300
    
        change parameter from string to char* on GetArrowType method
    
    commit 4f0807a7eb8d99e363ce07e33364acc164621a89
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 12:00:03 2021 -0300
    
        Remove status from sqlite use
    
    commit 05d6280326872b3f7f75bc51b8e8f91ef49979c1
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Oct 6 11:59:30 2021 -0300
    
        Move anonymous function to the top of file
    
    commit d962a33462b2061e9926a540f016d761247fbec5
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 16:04:37 2021 -0300
    
        Fix checkstyle
    
    commit 9f90bdec11c5827d6ac790f5345a2707b5d1b9a5
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 16:01:52 2021 -0300
    
        Refactor constructor from the class sqlite_tables_schema_batch_reader
    
    commit 61af7d444e635855fe0854dfba49d26cf75f38ac
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 16:01:22 2021 -0300
    
        Fix import order
    
    commit 5e2071c84e3775f09814548655986456bd257f1a
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 16:01:11 2021 -0300
    
        Decouple method PrepareQueryForGetTables from the class
    
    commit 449938b94b5b1d4ba241ec31ad57ee3ab91aee15
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Oct 5 16:00:16 2021 -0300
    
        Remove extra space
    
    commit e5ea5eba96bb9fbce298454441cd2d62cd7a1b7c
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 1 18:15:41 2021 -0300
    
        Pass the query to the batch reader vector for the table with schemas
    
    commit abe5af8dd3412531a46aa35fa03d02b78200481f
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 1 18:15:14 2021 -0300
    
        Invert order of vector on tests
    
    commit 4a99053103e53a08c6f8398d3473351ea8f450b2
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Oct 1 18:14:15 2021 -0300
    
        Add method prepareQuery
    
    commit bd19a660f588e6a4e17907a6ff972e05407aa0cf
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 16:45:24 2021 -0300
    
        Fix checkstyle
    
    commit 9a838a9dd04279526b5b3d25b683556f0526b6c6
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 16:44:07 2021 -0300
    
        Update CMakeLists.txt
    
    commit 574878f2e7cec50658b19fd3668a0755795d8769
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 16:43:57 2021 -0300
    
        Set values from catalogs and schema as null
    
    commit 0aa1cab6dd1743d59ed5f9252b7fc3cd54649e4e
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 16:43:41 2021 -0300
    
        Modify Macro from builder to deal with null values
    
    commit b8d8396a1b2585d4f02895c91a850fe90d559550
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:39:52 2021 -0300
    
        change constructor initialization
    
    commit 8e846c84631e6dcf2dce99d129e8b788c12aae57
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:39:18 2021 -0300
    
        Fix checkstyle
    
    commit 60b8326c29411d709dcf43b4b2aca9277d8baa99
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:09:11 2021 -0300
    
        Add more test for the GetTables
    
    commit 5bc0e9f2449cad3f128a936194682c7e52f9b8cf
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:08:57 2021 -0300
    
        Add a DECLARE_BINARY_ARRAY for testing
    
    commit 01266d72b56edc38071b83bfcabc8257be277cfc
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:08:23 2021 -0300
    
        Add table type filter to the query
    
    commit 8a02752d9f1ef897937e0c48f98c42213f2f8672
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 15:07:45 2021 -0300
    
        Refactor methods that parse the table type to field type
    
    commit a0f32ba9ab4bf390110d772c353592f018d5f692
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 30 09:59:53 2021 -0300
    
        Refactor test from GetTables
    
    commit 8a3dc6d8e20cf8826849453607dae92b83d6050d
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 29 11:28:54 2021 -0300
    
        Add new tests to GetTables
    
    commit aef712a73a4640c76088e08cd8ca339b934e16e0
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 29 11:28:41 2021 -0300
    
        Add new filter to the query on GetTables
    
    commit 814dc10579876ef44017a9bb74eec0efef224fd7
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 29 11:28:23 2021 -0300
    
        Refactor the name of class sqlite table schema batch reader
    
    commit 7d53dd6a3343569fca9279e9968c5f7678895fcc
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 28 16:09:39 2021 -0300
    
        Add class to tge CMakeLists.txt
    
    commit 86a0ba0b3d1a186dab33c48ffedaa1ebbbcfbf63
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 28 16:09:27 2021 -0300
    
        Add the schemas to be used by the GetTabkes
    
    commit 6b6d9c2b9bc13d2a46b13dce81f9f16f723fbaf8
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 28 16:09:09 2021 -0300
    
        Include methods GetTablesFlightInfo and DoGetTables
    
    commit 203e64f1116072e06874430be839832824965f04
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 28 16:08:46 2021 -0300
    
        Create a class to deal the DoGetTables when schema is included
    
    commit cac2d9fb56408e223be9ea0ca76582e57a0f2223
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Oct 6 15:11:16 2021 -0300
    
        Fix style issues
    
    commit c750b15a1c1d7543517f82bb3fe2594ad06d9e5b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Oct 6 15:11:16 2021 -0300
    
        Fix style issues
    
    commit b22fe92358cc1a8c5d7342ae0fda7101fbd00d19
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Sep 30 11:14:06 2021 -0300
    
        Undo unscoped changes to other files
    
    commit e68f6e689d366a3b2299f7c3d7e948de724735d7
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 29 17:16:52 2021 -0300
    
        Implement CommandGetSchemas
    
    commit f1d9f9da9d297c8e17979623d32f6aeec95de654
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Sep 30 15:39:43 2021 -0300
    
        Fix checkstyle errors
    
    commit 2fdc7c84cdfc1236baaf1f2fb478e6c8048d260f
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Sep 30 13:17:10 2021 -0300
    
        Fix problem when linking protobuf to flight-sql targets
    
    commit 5e57cd15cb480bbd6f743009a6a763c8a8e26f0b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Oct 5 13:14:55 2021 -0300
    
        Implement CommandGetSchemas
    
    commit b8f5dda429b7aebbcc936270d3123ff0cc27f27d
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 29 17:42:15 2021 -0300
    
        Make GetCatalogs return empty results
    
    commit a631b4f97f43f9f7074888c6b0e158df13bc93e5
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 22:30:11 2021 -0300
    
        Add comment about hardcoded GetCatalogs implementation
    
    commit d754fdec6fbd724c50c81024d3e00a8bd7bfcd9a
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 19:06:10 2021 -0300
    
        Fix minor comments on PR
    
    commit 060e9b45998512f53d30f072693194a00c53e104
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 16:04:32 2021 -0300
    
        Refactor tests to reduce duplication
    
    commit 7add18f4f40f7865298c31f7a3a523be2e191f9e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 15:07:40 2021 -0300
    
        Implement CommandGetCatalogs
    
    commit 5d3bc66c200abc30fd421ac7eb0c1c6bb87985cb
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 16:30:11 2021 -0300
    
        Improve readability for SqliteFlightSqlServer setup
    
    commit 68bec018bd9784327d27dc80b80c84e18077606c
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 16:07:07 2021 -0300
    
        Fix minor comments on PR
    
    commit 93c5a4ba70e2dd6b2083c8235255af095a3a703b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 14:27:29 2021 -0300
    
        Fix checkstyle errors
    
    commit 2232dd67f3829aa61feea031809dad4543e75c21
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Sep 27 16:36:06 2021 -0300
    
        Add integration tests for Flight SQL server example
    
    commit f7b6461ee156d05e602f4af287d1ae7be54794f2
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Sep 24 16:04:58 2021 -0300
    
        Add documentation to example classes
    
    commit 3584fc07f7fd76ab46ab76215d609cb35d344c91
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Sep 24 15:42:35 2021 -0300
    
        Add missing sqlite3 dependency on vcpkg.json
    
    commit 87fc8aae9cb0e5819dadc2f3a1ba394904f2acc2
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Sep 24 15:38:02 2021 -0300
    
        Implement Flight SQL example server using SQLite3
    
    commit 9dd9a3216c735183039cd87fb9d077433fec17f1
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Sep 28 14:00:22 2021 -0300
    
        Change default Status return on GetFlightInfo and DoGet
    
    commit 5bd548fb8f720397c1e7756151ac8c68190741c2
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Fri Sep 24 15:35:25 2021 -0300
    
        Remove empty constructor and destructor from FlightSqlServerBase header
    
    commit cffd1f4cbb5d9199bb47807e7fef131ac4359a7a
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Sep 24 15:20:36 2021 -0300
    
        Separate implementation from header file
    
    commit 685ccd00406593a35ee4156004db5a4b9a499c03
    Author: Jose Almeida <al...@gmail.com>
    Date:   Thu Sep 23 11:38:27 2021 -0300
    
        Add missing else if on DoGet method
    
    commit 4099ad21518a087ed818587be3e73ef84368398e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 22 15:16:52 2021 -0300
    
        Improve documentation on sql_server.h
    
    commit 8aaee1edcf1e248bb62ec4c81e0ef52097879ec0
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 22 14:29:16 2021 -0300
    
        Fix wrong arguments on server header file
    
    commit 5f7a4c81195c7bf688e1f82cef8781f7299d89d0
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 22 14:13:36 2021 -0300
    
        Implement missing branches on DoGet
    
    commit c5d63016cf700fdd3bcd3526011608ac0482c652
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 22 14:11:58 2021 -0300
    
        Add more statement to the getFlightInfo methods
    
    commit a25bb904c90726473816ba9d6fc11be1441e111c
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 22 14:00:27 2021 -0300
    
        Remove doPut* methods (not used yet)
    
    commit eeb4a3531d607d9a57513a04ec98c5282e7cc681
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 22 13:56:52 2021 -0300
    
        Remove flight-sql from CMakeLists.txt
    
    commit 03425db52435102f9fa40e20da3d497c4d0410f8
    Author: Jose Almeida <al...@gmail.com>
    Date:   Wed Sep 22 13:52:52 2021 -0300
    
        Add flight-sql server header file
    
    commit bf5cfeff437e045cbf38945c82f64bcb8fbc5f2f
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Sep 20 15:25:42 2021 -0300
    
        Change ExecuteUpdate output argument to raw pointer instead of unique_ptr<int64_t>
    
    commit bd2890ee117d64bca9856b3705eadd4eecd95274
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Sep 17 16:24:24 2021 -0300
    
        Change order of call from ExecuteUpdate on test_app.cc
    
    commit 5e11c8243312d6c1dc7cb4723ada6f2cff739940
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Sep 17 16:11:47 2021 -0300
    
        Checkstyle fix on flight-sql
    
    commit 59e1c2897fc2adb0891072faad09c92745bb18c5
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Sep 17 16:11:36 2021 -0300
    
        Change order of output parameters on methods from flight-sql
    
    commit 7aa4c14425b5b2628e16488166dcd9fe762454be
    Author: Jose Almeida <al...@gmail.com>
    Date:   Fri Sep 17 16:09:25 2021 -0300
    
        Change rows on executeUpdate to a unique_ptr
    
    commit 015d015ea7da2f8347c63b76c55768656b8d8f27
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Thu Sep 16 10:30:27 2021 -0300
    
        Refactor client_impl to reduce duplication on FlightDescriptor build
    
    commit 83db0122781b94c23557837661a27659ae8e4af7
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 14 16:56:07 2021 -0300
    
        Create a mock test for execute_update method on sql_client
    
    commit e078c057148c9ed91176036485e7c4a8363fb779
    Author: Jose Almeida <al...@gmail.com>
    Date:   Tue Sep 14 16:55:50 2021 -0300
    
        Implement executeUpdate logic on sql client
    
    commit 69dbdecccf977d6b3a6fb33a76047ab368af546e
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Wed Sep 15 19:33:25 2021 -0300
    
        [C++] Implement Flight SQL client test application (#121)
    
        * Implement Flight SQL client test application
    
        * Adjust PrintResults to consider multiple endpoints
    
        * Remove mentions to Dremio
    
        * Minor fix
    
        * Sort 'using' statements on test_app.cc
    
        * Transfer ownership of FLightClient to FlightSqlClient on constructor
    
        * Use reference instead of pointers on PrintResults
    
        * Make client methods to be const
    
    commit fde5c0022594146b971a635c337616ffe86e5bd1
    Author: Jose Almeida <53...@users.noreply.github.com>
    Date:   Tue Sep 14 11:42:48 2021 -0300
    
        [C++] Implements methods from flight-sql-client  (#120)
    
        * add a header file to the sql-client
    
        * Implements methods from sql-client
    
        * Add configuration files to the flight-sql
    
        * Change getFlightInfo to virtual and its constructor to protected
    
        * Create a mock test for getCatalogs
    
        * Remove unused test from CMakeLists.txt
    
        * Fix checkstyle on flight-sql files
    
        * Fix duplicate tests execution
    
        * Add test for getSchema from flightsql
    
        * Update flight headers and implements getTable and getTableTypes
    
        * Add other unit tests for metadata methods
    
        * Fix checkstyle errors
    
        * Implement missing methods GetPrimaryKeys, GetImportedKeys and GetExportedKeys
    
        * Refactor flight-sql/client.cc implementation
    
        * Remove unimplemented ExecuteUpdate test
    
        * Add google/protobuf/message include to flight-sql-client
    
        * Undo changes on flight/client.h and use templates for mocking FlightClient on FlightSqlClient
    
        * Use string references where parameters can not be null
    
        * Reorder FlightSqlClient method arguments
    
        * Avoid needing to use diamond syntax on FlightSqlClient
    
        Co-authored-by: Rafael Telles <ra...@telles.dev>
    
    commit f3fe962c5838c345589320d7c559526650e87cde
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Sep 6 11:37:08 2021 -0300
    
        Fix checkstyle issues
    
    commit 2ed7b0efae2a6128f13920ed4e18d6d7d7f0d803
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Aug 24 14:12:37 2021 -0300
    
        WIP: Clean up changes
    
    commit 7dc836e789b390297e191471e4aa142386bf793b
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Tue Aug 24 13:49:32 2021 -0300
    
        Update FindArrowFlightSql.cmake
    
    commit 370c92ef1d9748feef8c61e2b19515a558124939
    Author: Rafael Telles <ra...@telles.dev>
    Date:   Mon Aug 23 15:28:53 2021 -0300
    
        WIP: Set up flight-sql project on cpp directory
---
 ci/docker/conda-cpp.dockerfile                     |   1 +
 ci/scripts/cpp_build.sh                            |   1 +
 cpp/CMakeLists.txt                                 |   4 +
 cpp/cmake_modules/DefineOptions.cmake              |   2 +
 cpp/cmake_modules/FindArrowFlightSql.cmake         |  93 +++
 cpp/cmake_modules/FindSQLite3Alt.cmake             |  43 +
 cpp/src/arrow/CMakeLists.txt                       |   4 +
 cpp/src/arrow/flight/CMakeLists.txt                |  18 +-
 .../arrow/flight/sql/ArrowFlightSqlConfig.cmake.in |  36 +
 cpp/src/arrow/flight/sql/CMakeLists.txt            | 100 +++
 cpp/src/arrow/flight/sql/api.h                     |  20 +
 cpp/src/arrow/flight/sql/arrow-flight-sql.pc.in    |  25 +
 cpp/src/arrow/flight/sql/client.cc                 | 425 ++++++++++
 cpp/src/arrow/flight/sql/client.h                  | 247 ++++++
 cpp/src/arrow/flight/sql/client_test.cc            | 515 ++++++++++++
 cpp/src/arrow/flight/sql/example/sqlite_server.cc  | 813 +++++++++++++++++++
 cpp/src/arrow/flight/sql/example/sqlite_server.h   | 142 ++++
 .../arrow/flight/sql/example/sqlite_sql_info.cc    | 223 ++++++
 cpp/src/arrow/flight/sql/example/sqlite_sql_info.h |  34 +
 .../arrow/flight/sql/example/sqlite_statement.cc   | 137 ++++
 .../arrow/flight/sql/example/sqlite_statement.h    |  73 ++
 .../sql/example/sqlite_statement_batch_reader.cc   | 189 +++++
 .../sql/example/sqlite_statement_batch_reader.h    |  65 ++
 .../example/sqlite_tables_schema_batch_reader.cc   | 106 +++
 .../example/sqlite_tables_schema_batch_reader.h    |  58 ++
 cpp/src/arrow/flight/sql/server.cc                 | 761 ++++++++++++++++++
 cpp/src/arrow/flight/sql/server.h                  | 443 ++++++++++
 cpp/src/arrow/flight/sql/server_test.cc            | 767 ++++++++++++++++++
 cpp/src/arrow/flight/sql/sql_info_internal.cc      | 101 +++
 cpp/src/arrow/flight/sql/sql_info_internal.h       |  87 ++
 cpp/src/arrow/flight/sql/test_app_cli.cc           | 197 +++++
 cpp/src/arrow/flight/sql/test_server_cli.cc        |  63 ++
 cpp/src/arrow/flight/sql/types.h                   | 890 +++++++++++++++++++++
 cpp/vcpkg.json                                     |   1 +
 docker-compose.yml                                 |   3 +
 35 files changed, 6686 insertions(+), 1 deletion(-)

diff --git a/ci/docker/conda-cpp.dockerfile b/ci/docker/conda-cpp.dockerfile
index 8fd5e46..9363e67 100644
--- a/ci/docker/conda-cpp.dockerfile
+++ b/ci/docker/conda-cpp.dockerfile
@@ -41,6 +41,7 @@ ENV ARROW_BUILD_TESTS=ON \
     ARROW_DATASET=ON \
     ARROW_DEPENDENCY_SOURCE=CONDA \
     ARROW_FLIGHT=ON \
+    ARROW_FLIGHT_SQL=ON \
     ARROW_GANDIVA=ON \
     ARROW_HOME=$CONDA_PREFIX \
     ARROW_ORC=ON \
diff --git a/ci/scripts/cpp_build.sh b/ci/scripts/cpp_build.sh
index f791ddd..02718e5 100755
--- a/ci/scripts/cpp_build.sh
+++ b/ci/scripts/cpp_build.sh
@@ -70,6 +70,7 @@ cmake \
   -DARROW_EXTRA_ERROR_CONTEXT=${ARROW_EXTRA_ERROR_CONTEXT:-OFF} \
   -DARROW_FILESYSTEM=${ARROW_FILESYSTEM:-ON} \
   -DARROW_FLIGHT=${ARROW_FLIGHT:-OFF} \
+  -DARROW_FLIGHT_SQL=${ARROW_FLIGHT_SQL:-OFF} \
   -DARROW_FUZZING=${ARROW_FUZZING:-OFF} \
   -DARROW_GANDIVA_JAVA=${ARROW_GANDIVA_JAVA:-OFF} \
   -DARROW_GANDIVA_PC_CXX_FLAGS=${ARROW_GANDIVA_PC_CXX_FLAGS:-} \
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 0262357..e2b9f4e 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -334,6 +334,10 @@ if(ARROW_GANDIVA)
   set(ARROW_WITH_RE2 ON)
 endif()
 
+if(ARROW_FLIGHT_SQL)
+  set(ARROW_FLIGHT ON)
+endif()
+
 if(ARROW_CUDA
    OR ARROW_FLIGHT
    OR ARROW_PARQUET
diff --git a/cpp/cmake_modules/DefineOptions.cmake b/cpp/cmake_modules/DefineOptions.cmake
index f2ddff3..2afbdab 100644
--- a/cpp/cmake_modules/DefineOptions.cmake
+++ b/cpp/cmake_modules/DefineOptions.cmake
@@ -226,6 +226,8 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
   define_option(ARROW_FLIGHT
                 "Build the Arrow Flight RPC System (requires GRPC, Protocol Buffers)" OFF)
 
+  define_option(ARROW_FLIGHT_SQL "Build the Arrow Flight SQL extension" OFF)
+
   define_option(ARROW_GANDIVA "Build the Gandiva libraries" OFF)
 
   define_option(ARROW_GCS
diff --git a/cpp/cmake_modules/FindArrowFlightSql.cmake b/cpp/cmake_modules/FindArrowFlightSql.cmake
new file mode 100644
index 0000000..cbca81c
--- /dev/null
+++ b/cpp/cmake_modules/FindArrowFlightSql.cmake
@@ -0,0 +1,93 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+
+# - Find Arrow Flight SQL
+#
+# This module requires Arrow from which it uses
+#  arrow_find_package()
+#
+# This module defines
+#  ARROW_FLIGHT_SQL_FOUND, whether Flight has been found
+#  ARROW_FLIGHT_SQL_IMPORT_LIB,
+#    path to libarrow_flight's import library (Windows only)
+#  ARROW_FLIGHT_SQL_INCLUDE_DIR, directory containing headers
+#  ARROW_FLIGHT_SQL_LIBS, deprecated. Use ARROW_FLIGHT_SQL_LIB_DIR instead
+#  ARROW_FLIGHT_SQL_LIB_DIR, directory containing Flight libraries
+#  ARROW_FLIGHT_SQL_SHARED_IMP_LIB, deprecated. Use ARROW_FLIGHT_SQL_IMPORT_LIB instead
+#  ARROW_FLIGHT_SQL_SHARED_LIB, path to libarrow_flight's shared library
+#  ARROW_FLIGHT_SQL_STATIC_LIB, path to libarrow_flight.a
+
+if(DEFINED ARROW_FLIGHT_SQL_FOUND)
+  return()
+endif()
+
+set(find_package_arguments)
+if(${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION)
+  list(APPEND find_package_arguments "${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION}")
+endif()
+if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
+  list(APPEND find_package_arguments REQUIRED)
+endif()
+if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
+  list(APPEND find_package_arguments QUIET)
+endif()
+find_package(Arrow ${find_package_arguments})
+
+if(ARROW_FOUND)
+  arrow_find_package(ARROW_FLIGHT_SQL
+                     "${ARROW_HOME}"
+                     arrow_flight_sql
+                     arrow/flight/sql/api.h
+                     ArrowFlightSql
+                     arrow-flight-sql)
+  if(NOT ARROW_FLIGHT_SQL_VERSION)
+    set(ARROW_FLIGHT_SQL_VERSION "${ARROW_VERSION}")
+  endif()
+endif()
+
+if("${ARROW_FLIGHT_SQL_VERSION}" VERSION_EQUAL "${ARROW_VERSION}")
+  set(ARROW_FLIGHT_SQL_VERSION_MATCH TRUE)
+else()
+  set(ARROW_FLIGHT_SQL_VERSION_MATCH FALSE)
+endif()
+
+mark_as_advanced(ARROW_FLIGHT_SQL_IMPORT_LIB
+                 ARROW_FLIGHT_SQL_INCLUDE_DIR
+                 ARROW_FLIGHT_SQL_LIBS
+                 ARROW_FLIGHT_SQL_LIB_DIR
+                 ARROW_FLIGHT_SQL_SHARED_IMP_LIB
+                 ARROW_FLIGHT_SQL_SHARED_LIB
+                 ARROW_FLIGHT_SQL_STATIC_LIB
+                 ARROW_FLIGHT_SQL_VERSION
+                 ARROW_FLIGHT_SQL_VERSION_MATCH)
+
+find_package_handle_standard_args(
+  ArrowFlightSql
+  REQUIRED_VARS ARROW_FLIGHT_SQL_INCLUDE_DIR ARROW_FLIGHT_SQL_LIB_DIR
+                ARROW_FLIGHT_SQL_VERSION_MATCH
+  VERSION_VAR ARROW_FLIGHT_SQL_VERSION)
+set(ARROW_FLIGHT_SQL_FOUND ${ArrowFlightSql_FOUND})
+
+if(ArrowFlightSql_FOUND AND NOT ArrowFlightSql_FIND_QUIETLY)
+  message(STATUS "Found the Arrow Flight SQL by ${ARROW_FLIGHT_SQL_FIND_APPROACH}")
+  message(STATUS "Found the Arrow Flight SQL shared library: ${ARROW_FLIGHT_SQL_SHARED_LIB}"
+  )
+  message(STATUS "Found the Arrow Flight SQL import library: ${ARROW_FLIGHT_SQL_IMPORT_LIB}"
+  )
+  message(STATUS "Found the Arrow Flight SQL static library: ${ARROW_FLIGHT_SQL_STATIC_LIB}"
+  )
+endif()
diff --git a/cpp/cmake_modules/FindSQLite3Alt.cmake b/cpp/cmake_modules/FindSQLite3Alt.cmake
new file mode 100644
index 0000000..73a45f0
--- /dev/null
+++ b/cpp/cmake_modules/FindSQLite3Alt.cmake
@@ -0,0 +1,43 @@
+# 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.
+
+# Once done this will define
+# - FindSQLite3Alt
+#
+# This module will set the following variables if found:
+#  SQLite3_INCLUDE_DIRS  - SQLite3 include dir.
+#  SQLite3_LIBRARIES     - List of libraries when using SQLite3.
+#  SQLite3_FOUND         - True if SQLite3 found.
+#
+# Usage of this module as follows:
+# find_package(SQLite3Alt)
+
+find_path(SQLite3_INCLUDE_DIR sqlite3.h)
+find_library(SQLite3_LIBRARY NAMES sqlite3)
+
+# handle the QUIETLY and REQUIRED arguments and set SQLite3_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(SQLite3Alt REQUIRED_VARS SQLite3_LIBRARY
+                                                           SQLite3_INCLUDE_DIR)
+
+mark_as_advanced(SQLite3_LIBRARY SQLite3_INCLUDE_DIR)
+
+if(SQLite3Alt_FOUND)
+  set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR})
+  set(SQLite3_LIBRARIES ${SQLite3_LIBRARY})
+endif()
diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt
index 5736c55..502629a 100644
--- a/cpp/src/arrow/CMakeLists.txt
+++ b/cpp/src/arrow/CMakeLists.txt
@@ -732,6 +732,10 @@ if(ARROW_FLIGHT)
   add_subdirectory(flight)
 endif()
 
+if(ARROW_FLIGHT_SQL)
+  add_subdirectory(flight/sql)
+endif()
+
 if(ARROW_HIVESERVER2)
   add_subdirectory(dbi/hiveserver2)
 endif()
diff --git a/cpp/src/arrow/flight/CMakeLists.txt b/cpp/src/arrow/flight/CMakeLists.txt
index 8a3228e..55e89b2 100644
--- a/cpp/src/arrow/flight/CMakeLists.txt
+++ b/cpp/src/arrow/flight/CMakeLists.txt
@@ -25,7 +25,23 @@ if(WIN32)
   list(APPEND ARROW_FLIGHT_LINK_LIBS ws2_32.lib)
 endif()
 
-if(ARROW_TEST_LINKAGE STREQUAL "static")
+set(ARROW_FLIGHT_TEST_LINKAGE
+    "${ARROW_TEST_LINKAGE}"
+    PARENT_SCOPE)
+if(Protobuf_USE_STATIC_LIBS)
+  message(STATUS "Linking Arrow Flight tests statically due to static Protobuf")
+  set(ARROW_FLIGHT_TEST_LINKAGE
+      "static"
+      PARENT_SCOPE)
+endif()
+if(NOT ARROW_GRPC_USE_SHARED)
+  message(STATUS "Linking Arrow Flight tests statically due to static gRPC")
+  set(ARROW_FLIGHT_TEST_LINKAGE
+      "static"
+      PARENT_SCOPE)
+endif()
+
+if(ARROW_FLIGHT_TEST_LINKAGE STREQUAL "static")
   set(ARROW_FLIGHT_TEST_LINK_LIBS
       arrow_flight_static arrow_flight_testing_static ${ARROW_FLIGHT_STATIC_LINK_LIBS}
       ${ARROW_TEST_LINK_LIBS})
diff --git a/cpp/src/arrow/flight/sql/ArrowFlightSqlConfig.cmake.in b/cpp/src/arrow/flight/sql/ArrowFlightSqlConfig.cmake.in
new file mode 100644
index 0000000..1658f44
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/ArrowFlightSqlConfig.cmake.in
@@ -0,0 +1,36 @@
+# 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.
+#
+# This config sets the following variables in your project::
+#
+#   ArrowFlightSql_FOUND - true if Arrow Flight SQL found on the system
+#
+# This config sets the following targets in your project::
+#
+#   arrow_flight_sql_shared - for linked as shared library if shared library is built
+#   arrow_flight_sql_static - for linked as static library if static library is built
+
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+find_dependency(ArrowFlight)
+
+# Load targets only once. If we load targets multiple times, CMake reports
+# already existent target error.
+if(NOT (TARGET arrow_flight_sql_shared OR TARGET arrow_flight_sql_static))
+  include("${CMAKE_CURRENT_LIST_DIR}/ArrowFlightSqlTargets.cmake")
+endif()
diff --git a/cpp/src/arrow/flight/sql/CMakeLists.txt b/cpp/src/arrow/flight/sql/CMakeLists.txt
new file mode 100644
index 0000000..4a31f5b
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/CMakeLists.txt
@@ -0,0 +1,100 @@
+# 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.
+
+add_custom_target(arrow_flight_sql)
+
+arrow_install_all_headers("arrow/flight/sql")
+
+set(FLIGHT_SQL_PROTO_PATH "${ARROW_SOURCE_DIR}/../format")
+set(FLIGHT_SQL_PROTO ${ARROW_SOURCE_DIR}/../format/FlightSql.proto)
+
+set(FLIGHT_SQL_GENERATED_PROTO_FILES "${CMAKE_CURRENT_BINARY_DIR}/FlightSql.pb.cc"
+                                     "${CMAKE_CURRENT_BINARY_DIR}/FlightSql.pb.h")
+
+set(PROTO_DEPENDS ${FLIGHT_SQL_PROTO} ${ARROW_PROTOBUF_LIBPROTOBUF})
+
+add_custom_command(OUTPUT ${FLIGHT_SQL_GENERATED_PROTO_FILES}
+                   COMMAND ${ARROW_PROTOBUF_PROTOC} "-I${FLIGHT_SQL_PROTO_PATH}"
+                           "--cpp_out=${CMAKE_CURRENT_BINARY_DIR}" "${FLIGHT_SQL_PROTO}"
+                   DEPENDS ${PROTO_DEPENDS})
+
+set_source_files_properties(${FLIGHT_SQL_GENERATED_PROTO_FILES} PROPERTIES GENERATED TRUE)
+
+add_custom_target(flight_sql_protobuf_gen ALL DEPENDS ${FLIGHT_SQL_GENERATED_PROTO_FILES})
+
+set(ARROW_FLIGHT_SQL_SRCS server.cc sql_info_internal.cc client.cc
+                          "${CMAKE_CURRENT_BINARY_DIR}/FlightSql.pb.cc")
+
+add_arrow_lib(arrow_flight_sql
+              CMAKE_PACKAGE_NAME
+              ArrowFlightSql
+              PKG_CONFIG_NAME
+              arrow-flight-sql
+              OUTPUTS
+              ARROW_FLIGHT_SQL_LIBRARIES
+              SOURCES
+              ${ARROW_FLIGHT_SQL_SRCS}
+              DEPENDENCIES
+              flight_sql_protobuf_gen
+              SHARED_LINK_FLAGS
+              ${ARROW_VERSION_SCRIPT_FLAGS} # Defined in cpp/arrow/CMakeLists.txt
+              SHARED_LINK_LIBS
+              arrow_flight_shared
+              STATIC_LINK_LIBS
+              arrow_flight_static)
+
+if(ARROW_FLIGHT_TEST_LINKAGE STREQUAL "static")
+  set(ARROW_FLIGHT_SQL_TEST_LINK_LIBS
+      arrow_flight_sql_static arrow_flight_testing_static
+      ${ARROW_FLIGHT_STATIC_LINK_LIBS} ${ARROW_TEST_LINK_LIBS})
+else()
+  set(ARROW_FLIGHT_SQL_TEST_LINK_LIBS arrow_flight_sql_shared arrow_flight_testing_shared
+                                      ${ARROW_TEST_LINK_LIBS})
+endif()
+
+# Build test server for unit tests
+if(ARROW_BUILD_TESTS OR ARROW_BUILD_EXAMPLES)
+  find_package(SQLite3Alt REQUIRED)
+
+  set(ARROW_FLIGHT_SQL_TEST_SERVER_SRCS
+      example/sqlite_sql_info.cc
+      example/sqlite_statement.cc
+      example/sqlite_statement_batch_reader.cc
+      example/sqlite_server.cc
+      example/sqlite_tables_schema_batch_reader.cc)
+
+  add_arrow_test(flight_sql_test
+                 SOURCES
+                 client_test.cc
+                 server_test.cc
+                 ${ARROW_FLIGHT_SQL_TEST_SERVER_SRCS}
+                 STATIC_LINK_LIBS
+                 ${ARROW_FLIGHT_SQL_TEST_LINK_LIBS}
+                 ${SQLite3_LIBRARIES}
+                 LABELS
+                 "arrow_flight_sql")
+
+  add_executable(flight_sql_test_server test_server_cli.cc
+                                        ${ARROW_FLIGHT_SQL_TEST_SERVER_SRCS})
+  target_link_libraries(flight_sql_test_server
+                        PRIVATE ${ARROW_FLIGHT_SQL_TEST_LINK_LIBS} ${GFLAGS_LIBRARIES}
+                                ${SQLite3_LIBRARIES})
+
+  add_executable(flight_sql_test_app test_app_cli.cc)
+  target_link_libraries(flight_sql_test_app PRIVATE ${ARROW_FLIGHT_SQL_TEST_LINK_LIBS}
+                                                    ${GFLAGS_LIBRARIES})
+endif()
diff --git a/cpp/src/arrow/flight/sql/api.h b/cpp/src/arrow/flight/sql/api.h
new file mode 100644
index 0000000..3b909ee
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/api.h
@@ -0,0 +1,20 @@
+// 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.
+
+#pragma once
+
+#include "arrow/flight/sql/client.h"
diff --git a/cpp/src/arrow/flight/sql/arrow-flight-sql.pc.in b/cpp/src/arrow/flight/sql/arrow-flight-sql.pc.in
new file mode 100644
index 0000000..6d4eab0
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/arrow-flight-sql.pc.in
@@ -0,0 +1,25 @@
+# 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.
+
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+
+Name: Apache Arrow Flight SQL
+Description: Apache Arrow Flight SQL extension
+Version: @ARROW_VERSION@
+Requires: arrow-flight
+Libs: -L${libdir} -larrow_flight_sql
diff --git a/cpp/src/arrow/flight/sql/client.cc b/cpp/src/arrow/flight/sql/client.cc
new file mode 100644
index 0000000..50a5777
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/client.cc
@@ -0,0 +1,425 @@
+// 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.
+
+#include "arrow/flight/sql/client.h"
+
+#include <google/protobuf/any.pb.h>
+
+#include "arrow/buffer.h"
+#include "arrow/flight/sql/FlightSql.pb.h"
+#include "arrow/flight/types.h"
+#include "arrow/io/memory.h"
+#include "arrow/ipc/reader.h"
+#include "arrow/result.h"
+#include "arrow/testing/gtest_util.h"
+#include "arrow/util/logging.h"
+
+namespace flight_sql_pb = arrow::flight::protocol::sql;
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+FlightSqlClient::FlightSqlClient(std::shared_ptr<FlightClient> client)
+    : impl_(std::move(client)) {}
+
+PreparedStatement::PreparedStatement(FlightSqlClient* client, std::string handle,
+                                     std::shared_ptr<Schema> dataset_schema,
+                                     std::shared_ptr<Schema> parameter_schema,
+                                     FlightCallOptions options)
+    : client_(client),
+      options_(std::move(options)),
+      handle_(std::move(handle)),
+      dataset_schema_(std::move(dataset_schema)),
+      parameter_schema_(std::move(parameter_schema)),
+      is_closed_(false) {}
+
+PreparedStatement::~PreparedStatement() {
+  if (IsClosed()) return;
+
+  const Status status = Close();
+  if (!status.ok()) {
+    ARROW_LOG(ERROR) << "Failed to delete PreparedStatement: " << status.ToString();
+  }
+}
+
+inline FlightDescriptor GetFlightDescriptorForCommand(
+    const google::protobuf::Message& command) {
+  google::protobuf::Any any;
+  any.PackFrom(command);
+
+  const std::string& string = any.SerializeAsString();
+  return FlightDescriptor::Command(string);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoForCommand(
+    FlightSqlClient& client, const FlightCallOptions& options,
+    const google::protobuf::Message& command) {
+  const FlightDescriptor& descriptor = GetFlightDescriptorForCommand(command);
+
+  ARROW_ASSIGN_OR_RAISE(auto flight_info, client.GetFlightInfo(options, descriptor));
+  return std::move(flight_info);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::Execute(
+    const FlightCallOptions& options, const std::string& query) {
+  flight_sql_pb::CommandStatementQuery command;
+  command.set_query(query);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<int64_t> FlightSqlClient::ExecuteUpdate(const FlightCallOptions& options,
+                                                      const std::string& query) {
+  flight_sql_pb::CommandStatementUpdate command;
+  command.set_query(query);
+
+  const FlightDescriptor& descriptor = GetFlightDescriptorForCommand(command);
+
+  std::unique_ptr<FlightStreamWriter> writer;
+  std::unique_ptr<FlightMetadataReader> reader;
+
+  ARROW_RETURN_NOT_OK(DoPut(options, descriptor, NULLPTR, &writer, &reader));
+
+  std::shared_ptr<Buffer> metadata;
+
+  ARROW_RETURN_NOT_OK(reader->ReadMetadata(&metadata));
+
+  flight_sql_pb::DoPutUpdateResult doPutUpdateResult;
+
+  flight_sql_pb::DoPutUpdateResult result;
+  if (!result.ParseFromArray(metadata->data(), static_cast<int>(metadata->size()))) {
+    return Status::Invalid("Unable to parse DoPutUpdateResult object.");
+  }
+
+  return result.record_count();
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetCatalogs(
+    const FlightCallOptions& options) {
+  flight_sql_pb::CommandGetCatalogs command;
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetDbSchemas(
+    const FlightCallOptions& options, const std::string* catalog,
+    const std::string* db_schema_filter_pattern) {
+  flight_sql_pb::CommandGetDbSchemas command;
+  if (catalog != NULLPTR) {
+    command.set_catalog(*catalog);
+  }
+  if (db_schema_filter_pattern != NULLPTR) {
+    command.set_db_schema_filter_pattern(*db_schema_filter_pattern);
+  }
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetTables(
+    const FlightCallOptions& options, const std::string* catalog,
+    const std::string* db_schema_filter_pattern, const std::string* table_filter_pattern,
+    bool include_schema, const std::vector<std::string>* table_types) {
+  flight_sql_pb::CommandGetTables command;
+
+  if (catalog != NULLPTR) {
+    command.set_catalog(*catalog);
+  }
+
+  if (db_schema_filter_pattern != NULLPTR) {
+    command.set_db_schema_filter_pattern(*db_schema_filter_pattern);
+  }
+
+  if (table_filter_pattern != NULLPTR) {
+    command.set_table_name_filter_pattern(*table_filter_pattern);
+  }
+
+  command.set_include_schema(include_schema);
+
+  if (table_types != NULLPTR) {
+    for (const std::string& table_type : *table_types) {
+      command.add_table_types(table_type);
+    }
+  }
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetPrimaryKeys(
+    const FlightCallOptions& options, const TableRef& table_ref) {
+  flight_sql_pb::CommandGetPrimaryKeys command;
+
+  if (table_ref.catalog.has_value()) {
+    command.set_catalog(table_ref.catalog.value());
+  }
+
+  if (table_ref.db_schema.has_value()) {
+    command.set_db_schema(table_ref.db_schema.value());
+  }
+
+  command.set_table(table_ref.table);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetExportedKeys(
+    const FlightCallOptions& options, const TableRef& table_ref) {
+  flight_sql_pb::CommandGetExportedKeys command;
+
+  if (table_ref.catalog.has_value()) {
+    command.set_catalog(table_ref.catalog.value());
+  }
+
+  if (table_ref.db_schema.has_value()) {
+    command.set_db_schema(table_ref.db_schema.value());
+  }
+
+  command.set_table(table_ref.table);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetImportedKeys(
+    const FlightCallOptions& options, const TableRef& table_ref) {
+  flight_sql_pb::CommandGetImportedKeys command;
+
+  if (table_ref.catalog.has_value()) {
+    command.set_catalog(table_ref.catalog.value());
+  }
+
+  if (table_ref.db_schema.has_value()) {
+    command.set_db_schema(table_ref.db_schema.value());
+  }
+
+  command.set_table(table_ref.table);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetCrossReference(
+    const FlightCallOptions& options, const TableRef& pk_table_ref,
+    const TableRef& fk_table_ref) {
+  flight_sql_pb::CommandGetCrossReference command;
+
+  if (pk_table_ref.catalog.has_value()) {
+    command.set_pk_catalog(pk_table_ref.catalog.value());
+  }
+  if (pk_table_ref.db_schema.has_value()) {
+    command.set_pk_db_schema(pk_table_ref.db_schema.value());
+  }
+  command.set_pk_table(pk_table_ref.table);
+
+  if (fk_table_ref.catalog.has_value()) {
+    command.set_fk_catalog(fk_table_ref.catalog.value());
+  }
+  if (fk_table_ref.db_schema.has_value()) {
+    command.set_fk_db_schema(fk_table_ref.db_schema.value());
+  }
+  command.set_fk_table(fk_table_ref.table);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetTableTypes(
+    const FlightCallOptions& options) {
+  flight_sql_pb::CommandGetTableTypes command;
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+arrow::Result<std::unique_ptr<FlightStreamReader>> FlightSqlClient::DoGet(
+    const FlightCallOptions& options, const Ticket& ticket) {
+  std::unique_ptr<FlightStreamReader> stream;
+  ARROW_RETURN_NOT_OK(DoGet(options, ticket, &stream));
+
+  return std::move(stream);
+}
+
+arrow::Result<std::shared_ptr<PreparedStatement>> FlightSqlClient::Prepare(
+    const FlightCallOptions& options, const std::string& query) {
+  google::protobuf::Any command;
+  flight_sql_pb::ActionCreatePreparedStatementRequest request;
+  request.set_query(query);
+  command.PackFrom(request);
+
+  Action action;
+  action.type = "CreatePreparedStatement";
+  action.body = Buffer::FromString(command.SerializeAsString());
+
+  std::unique_ptr<ResultStream> results;
+
+  ARROW_RETURN_NOT_OK(DoAction(options, action, &results));
+
+  std::unique_ptr<Result> result;
+  ARROW_RETURN_NOT_OK(results->Next(&result));
+
+  google::protobuf::Any prepared_result;
+
+  std::shared_ptr<Buffer> message = std::move(result->body);
+  if (!prepared_result.ParseFromArray(message->data(),
+                                      static_cast<int>(message->size()))) {
+    return Status::Invalid("Unable to parse packed ActionCreatePreparedStatementResult");
+  }
+
+  flight_sql_pb::ActionCreatePreparedStatementResult prepared_statement_result;
+
+  if (!prepared_result.UnpackTo(&prepared_statement_result)) {
+    return Status::Invalid("Unable to unpack ActionCreatePreparedStatementResult");
+  }
+
+  const std::string& serialized_dataset_schema =
+      prepared_statement_result.dataset_schema();
+  const std::string& serialized_parameter_schema =
+      prepared_statement_result.parameter_schema();
+
+  std::shared_ptr<Schema> dataset_schema;
+  if (!serialized_dataset_schema.empty()) {
+    io::BufferReader dataset_schema_reader(serialized_dataset_schema);
+    ipc::DictionaryMemo in_memo;
+    ARROW_ASSIGN_OR_RAISE(dataset_schema, ReadSchema(&dataset_schema_reader, &in_memo));
+  }
+  std::shared_ptr<Schema> parameter_schema;
+  if (!serialized_parameter_schema.empty()) {
+    io::BufferReader parameter_schema_reader(serialized_parameter_schema);
+    ipc::DictionaryMemo in_memo;
+    ARROW_ASSIGN_OR_RAISE(parameter_schema,
+                          ReadSchema(&parameter_schema_reader, &in_memo));
+  }
+  auto handle = prepared_statement_result.prepared_statement_handle();
+
+  return std::make_shared<PreparedStatement>(this, handle, dataset_schema,
+                                             parameter_schema, options);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> PreparedStatement::Execute() {
+  if (is_closed_) {
+    return Status::Invalid("Statement already closed.");
+  }
+
+  flight_sql_pb::CommandPreparedStatementQuery execute_query_command;
+
+  execute_query_command.set_prepared_statement_handle(handle_);
+
+  google::protobuf::Any any;
+  any.PackFrom(execute_query_command);
+
+  const std::string& string = any.SerializeAsString();
+  const FlightDescriptor descriptor = FlightDescriptor::Command(string);
+
+  if (parameter_binding_ && parameter_binding_->num_rows() > 0) {
+    std::unique_ptr<FlightStreamWriter> writer;
+    std::unique_ptr<FlightMetadataReader> reader;
+    ARROW_RETURN_NOT_OK(client_->DoPut(options_, descriptor, parameter_binding_->schema(),
+                                       &writer, &reader));
+
+    ARROW_RETURN_NOT_OK(writer->WriteRecordBatch(*parameter_binding_));
+    ARROW_RETURN_NOT_OK(writer->DoneWriting());
+    // Wait for the server to ack the result
+    std::shared_ptr<Buffer> buffer;
+    ARROW_RETURN_NOT_OK(reader->ReadMetadata(&buffer));
+  }
+
+  ARROW_ASSIGN_OR_RAISE(auto flight_info, client_->GetFlightInfo(options_, descriptor));
+  return std::move(flight_info);
+}
+
+arrow::Result<int64_t> PreparedStatement::ExecuteUpdate() {
+  if (is_closed_) {
+    return Status::Invalid("Statement already closed.");
+  }
+
+  flight_sql_pb::CommandPreparedStatementUpdate command;
+  command.set_prepared_statement_handle(handle_);
+  const FlightDescriptor& descriptor = GetFlightDescriptorForCommand(command);
+  std::unique_ptr<FlightStreamWriter> writer;
+  std::unique_ptr<FlightMetadataReader> reader;
+
+  if (parameter_binding_ && parameter_binding_->num_rows() > 0) {
+    ARROW_RETURN_NOT_OK(client_->DoPut(options_, descriptor, parameter_binding_->schema(),
+                                       &writer, &reader));
+    ARROW_RETURN_NOT_OK(writer->WriteRecordBatch(*parameter_binding_));
+  } else {
+    const std::shared_ptr<Schema> schema = arrow::schema({});
+    ARROW_RETURN_NOT_OK(client_->DoPut(options_, descriptor, schema, &writer, &reader));
+    const auto& record_batch =
+        arrow::RecordBatch::Make(schema, 0, (std::vector<std::shared_ptr<Array>>){});
+    ARROW_RETURN_NOT_OK(writer->WriteRecordBatch(*record_batch));
+  }
+
+  ARROW_RETURN_NOT_OK(writer->DoneWriting());
+  std::shared_ptr<Buffer> metadata;
+  ARROW_RETURN_NOT_OK(reader->ReadMetadata(&metadata));
+  ARROW_RETURN_NOT_OK(writer->Close());
+
+  flight_sql_pb::DoPutUpdateResult result;
+  if (!result.ParseFromArray(metadata->data(), static_cast<int>(metadata->size()))) {
+    return Status::Invalid("Unable to parse DoPutUpdateResult object.");
+  }
+
+  return result.record_count();
+}
+
+Status PreparedStatement::SetParameters(std::shared_ptr<RecordBatch> parameter_binding) {
+  parameter_binding_ = std::move(parameter_binding);
+
+  return Status::OK();
+}
+
+bool PreparedStatement::IsClosed() const { return is_closed_; }
+
+std::shared_ptr<Schema> PreparedStatement::dataset_schema() const {
+  return dataset_schema_;
+}
+
+std::shared_ptr<Schema> PreparedStatement::parameter_schema() const {
+  return parameter_schema_;
+}
+
+Status PreparedStatement::Close() {
+  if (is_closed_) {
+    return Status::Invalid("Statement already closed.");
+  }
+  google::protobuf::Any command;
+  flight_sql_pb::ActionClosePreparedStatementRequest request;
+  request.set_prepared_statement_handle(handle_);
+
+  command.PackFrom(request);
+
+  Action action;
+  action.type = "ClosePreparedStatement";
+  action.body = Buffer::FromString(command.SerializeAsString());
+
+  std::unique_ptr<ResultStream> results;
+
+  ARROW_RETURN_NOT_OK(client_->DoAction(options_, action, &results));
+
+  is_closed_ = true;
+
+  return Status::OK();
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlClient::GetSqlInfo(
+    const FlightCallOptions& options, const std::vector<int>& sql_info) {
+  flight_sql_pb::CommandGetSqlInfo command;
+  for (const int& info : sql_info) command.add_info(info);
+
+  return GetFlightInfoForCommand(*this, options, command);
+}
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/client.h b/cpp/src/arrow/flight/sql/client.h
new file mode 100644
index 0000000..5bf1b3e
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/client.h
@@ -0,0 +1,247 @@
+// 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.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "arrow/flight/client.h"
+#include "arrow/flight/sql/types.h"
+#include "arrow/flight/types.h"
+#include "arrow/result.h"
+#include "arrow/status.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+class PreparedStatement;
+
+/// \brief Flight client with Flight SQL semantics.
+class ARROW_EXPORT FlightSqlClient {
+  friend class PreparedStatement;
+
+ private:
+  std::shared_ptr<FlightClient> impl_;
+
+ public:
+  explicit FlightSqlClient(std::shared_ptr<FlightClient> client);
+
+  virtual ~FlightSqlClient() = default;
+
+  /// \brief Execute a query on the server.
+  /// \param[in] options      RPC-layer hints for this call.
+  /// \param[in] query        The query to be executed in the UTF-8 format.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> Execute(const FlightCallOptions& options,
+                                                     const std::string& query);
+
+  /// \brief Execute an update query on the server.
+  /// \param[in] options      RPC-layer hints for this call.
+  /// \param[in] query        The query to be executed in the UTF-8 format.
+  /// \return The quantity of rows affected by the operation.
+  arrow::Result<int64_t> ExecuteUpdate(const FlightCallOptions& options,
+                                       const std::string& query);
+
+  /// \brief Request a list of catalogs.
+  /// \param[in] options      RPC-layer hints for this call.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetCatalogs(
+      const FlightCallOptions& options);
+
+  /// \brief Request a list of database schemas.
+  /// \param[in] options                   RPC-layer hints for this call.
+  /// \param[in] catalog                   The catalog.
+  /// \param[in] db_schema_filter_pattern  The schema filter pattern.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetDbSchemas(
+      const FlightCallOptions& options, const std::string* catalog,
+      const std::string* db_schema_filter_pattern);
+
+  /// \brief Given a flight ticket and schema, request to be sent the
+  /// stream. Returns record batch stream reader
+  /// \param[in] options Per-RPC options
+  /// \param[in] ticket The flight ticket to use
+  /// \return The returned RecordBatchReader
+  arrow::Result<std::unique_ptr<FlightStreamReader>> DoGet(
+      const FlightCallOptions& options, const Ticket& ticket);
+
+  /// \brief Request a list of tables.
+  /// \param[in] options                   RPC-layer hints for this call.
+  /// \param[in] catalog                   The catalog.
+  /// \param[in] db_schema_filter_pattern  The schema filter pattern.
+  /// \param[in] table_filter_pattern      The table filter pattern.
+  /// \param[in] include_schema            True to include the schema upon return,
+  ///                                      false to not include the schema.
+  /// \param[in] table_types               The table types to include.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetTables(
+      const FlightCallOptions& options, const std::string* catalog,
+      const std::string* db_schema_filter_pattern,
+      const std::string* table_filter_pattern, bool include_schema,
+      const std::vector<std::string>* table_types);
+
+  /// \brief Request the primary keys for a table.
+  /// \param[in] options          RPC-layer hints for this call.
+  /// \param[in] table_ref        The table reference.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetPrimaryKeys(
+      const FlightCallOptions& options, const TableRef& table_ref);
+
+  /// \brief Retrieves a description about the foreign key columns that reference the
+  /// primary key columns of the given table.
+  /// \param[in] options          RPC-layer hints for this call.
+  /// \param[in] table_ref        The table reference.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetExportedKeys(
+      const FlightCallOptions& options, const TableRef& table_ref);
+
+  /// \brief Retrieves the foreign key columns for the given table.
+  /// \param[in] options          RPC-layer hints for this call.
+  /// \param[in] table_ref        The table reference.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetImportedKeys(
+      const FlightCallOptions& options, const TableRef& table_ref);
+
+  /// \brief Retrieves a description of the foreign key columns in the given foreign key
+  ///        table that reference the primary key or the columns representing a unique
+  ///        constraint of the parent table (could be the same or a different table).
+  /// \param[in] options        RPC-layer hints for this call.
+  /// \param[in] pk_table_ref   The table reference that exports the key.
+  /// \param[in] fk_table_ref   The table reference that imports the key.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetCrossReference(
+      const FlightCallOptions& options, const TableRef& pk_table_ref,
+      const TableRef& fk_table_ref);
+
+  /// \brief Request a list of table types.
+  /// \param[in] options          RPC-layer hints for this call.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetTableTypes(
+      const FlightCallOptions& options);
+
+  /// \brief Request a list of SQL information.
+  /// \param[in] options RPC-layer hints for this call.
+  /// \param[in] sql_info the SQL info required.
+  /// \return The FlightInfo describing where to access the dataset.
+  arrow::Result<std::unique_ptr<FlightInfo>> GetSqlInfo(const FlightCallOptions& options,
+                                                        const std::vector<int>& sql_info);
+
+  /// \brief Create a prepared statement object.
+  /// \param[in] options              RPC-layer hints for this call.
+  /// \param[in] query                The query that will be executed.
+  /// \return The created prepared statement.
+  arrow::Result<std::shared_ptr<PreparedStatement>> Prepare(
+      const FlightCallOptions& options, const std::string& query);
+
+  /// \brief Retrieve the FlightInfo.
+  /// \param[in] options      RPC-layer hints for this call.
+  /// \param[in] descriptor   The flight descriptor.
+  /// \return The flight info with the metadata.
+  // NOTE: This is public because it is been used by the anonymous
+  // function GetFlightInfoForCommand.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfo(
+      const FlightCallOptions& options, const FlightDescriptor& descriptor) {
+    std::unique_ptr<FlightInfo> info;
+    ARROW_RETURN_NOT_OK(impl_->GetFlightInfo(options, descriptor, &info));
+
+    return info;
+  }
+
+ protected:
+  virtual Status DoPut(const FlightCallOptions& options,
+                       const FlightDescriptor& descriptor,
+                       const std::shared_ptr<Schema>& schema,
+                       std::unique_ptr<FlightStreamWriter>* stream,
+                       std::unique_ptr<FlightMetadataReader>* reader) {
+    return impl_->DoPut(options, descriptor, schema, stream, reader);
+  }
+
+  virtual Status DoGet(const FlightCallOptions& options, const Ticket& ticket,
+                       std::unique_ptr<FlightStreamReader>* stream) {
+    return impl_->DoGet(options, ticket, stream);
+  }
+
+  virtual Status DoAction(const FlightCallOptions& options, const Action& action,
+                          std::unique_ptr<ResultStream>* results) {
+    return impl_->DoAction(options, action, results);
+  }
+};
+
+/// \brief PreparedStatement class from flight sql.
+class ARROW_EXPORT PreparedStatement {
+  FlightSqlClient* client_;
+  FlightCallOptions options_;
+  std::string handle_;
+  std::shared_ptr<Schema> dataset_schema_;
+  std::shared_ptr<Schema> parameter_schema_;
+  std::shared_ptr<RecordBatch> parameter_binding_;
+  bool is_closed_;
+
+ public:
+  /// \brief Constructor for the PreparedStatement class.
+  /// \param[in] client                Client object used to make the RPC requests.
+  /// \param[in] handle                Handle for this prepared statement.
+  /// \param[in] dataset_schema        Schema of the resulting dataset.
+  /// \param[in] parameter_schema      Schema of the parameters (if any).
+  /// \param[in] options               RPC-layer hints for this call.
+  PreparedStatement(FlightSqlClient* client, std::string handle,
+                    std::shared_ptr<Schema> dataset_schema,
+                    std::shared_ptr<Schema> parameter_schema, FlightCallOptions options);
+
+  /// \brief Default destructor for the PreparedStatement class.
+  /// The destructor will call the Close method from the class in order,
+  /// to send a request to close the PreparedStatement.
+  /// NOTE: It is best to explicitly close the PreparedStatement, otherwise
+  /// errors can't be caught.
+  ~PreparedStatement();
+
+  /// \brief Executes the prepared statement query on the server.
+  /// \return A FlightInfo object representing the stream(s) to fetch.
+  arrow::Result<std::unique_ptr<FlightInfo>> Execute();
+
+  /// \brief Executes the prepared statement update query on the server.
+  /// \return The number of rows affected.
+  arrow::Result<int64_t> ExecuteUpdate();
+
+  /// \brief Retrieve the parameter schema from the query.
+  /// \return The parameter schema from the query.
+  std::shared_ptr<Schema> parameter_schema() const;
+
+  /// \brief Retrieve the ResultSet schema from the query.
+  /// \return The ResultSet schema from the query.
+  std::shared_ptr<Schema> dataset_schema() const;
+
+  /// \brief Set a RecordBatch that contains the parameters that will be bind.
+  /// \param parameter_binding   The parameters that will be bind.
+  /// \return                     Status.
+  Status SetParameters(std::shared_ptr<RecordBatch> parameter_binding);
+
+  /// \brief Close the prepared statement, so that this PreparedStatement can not used
+  /// anymore and server can free up any resources.
+  /// \return Status.
+  Status Close();
+
+  /// \brief Check if the prepared statement is closed.
+  /// \return The state of the prepared statement.
+  bool IsClosed() const;
+};
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/client_test.cc b/cpp/src/arrow/flight/sql/client_test.cc
new file mode 100644
index 0000000..8c0c833
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/client_test.cc
@@ -0,0 +1,515 @@
+// 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.
+
+#include "arrow/flight/client.h"
+
+#include <gmock/gmock.h>
+#include <google/protobuf/any.pb.h>
+#include <gtest/gtest.h>
+
+#include <utility>
+
+#include "arrow/flight/sql/FlightSql.pb.h"
+#include "arrow/flight/sql/api.h"
+#include "arrow/testing/gtest_util.h"
+
+namespace pb = arrow::flight::protocol;
+using ::testing::_;
+using ::testing::Ref;
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+class FlightSqlClientMock : public FlightSqlClient {
+ public:
+  FlightSqlClientMock() : FlightSqlClient(nullptr) {}
+
+  ~FlightSqlClientMock() = default;
+
+  MOCK_METHOD(arrow::Result<std::unique_ptr<FlightInfo>>, GetFlightInfo,
+              (const FlightCallOptions&, const FlightDescriptor&));
+  MOCK_METHOD(Status, DoGet,
+              (const FlightCallOptions& options, const Ticket& ticket,
+               std::unique_ptr<FlightStreamReader>* stream));
+  MOCK_METHOD(Status, DoPut,
+              (const FlightCallOptions&, const FlightDescriptor&,
+               const std::shared_ptr<Schema>& schema,
+               std::unique_ptr<FlightStreamWriter>*,
+               std::unique_ptr<FlightMetadataReader>*));
+  MOCK_METHOD(Status, DoAction,
+              (const FlightCallOptions& options, const Action& action,
+               std::unique_ptr<ResultStream>* results));
+};
+
+class TestFlightSqlClient : public ::testing::Test {
+ protected:
+  FlightSqlClientMock sql_client_;
+  FlightCallOptions call_options_;
+
+  void SetUp() override {}
+
+  void TearDown() override {}
+};
+
+class FlightMetadataReaderMock : public FlightMetadataReader {
+ public:
+  std::shared_ptr<Buffer>* buffer;
+
+  explicit FlightMetadataReaderMock(std::shared_ptr<Buffer>* buffer) {
+    this->buffer = buffer;
+  }
+
+  Status ReadMetadata(std::shared_ptr<Buffer>* out) override {
+    *out = *buffer;
+    return Status::OK();
+  }
+};
+
+class FlightStreamWriterMock : public FlightStreamWriter {
+ public:
+  FlightStreamWriterMock() = default;
+
+  Status DoneWriting() override { return Status::OK(); }
+
+  Status WriteMetadata(std::shared_ptr<Buffer> app_metadata) override {
+    return Status::OK();
+  }
+
+  Status Begin(const std::shared_ptr<Schema>& schema,
+               const ipc::IpcWriteOptions& options) override {
+    return Status::OK();
+  }
+
+  Status Begin(const std::shared_ptr<Schema>& schema) override {
+    return MetadataRecordBatchWriter::Begin(schema);
+  }
+
+  ipc::WriteStats stats() const override { return ipc::WriteStats(); }
+
+  Status WriteWithMetadata(const RecordBatch& batch,
+                           std::shared_ptr<Buffer> app_metadata) override {
+    return Status::OK();
+  }
+
+  Status Close() override { return Status::OK(); }
+
+  Status WriteRecordBatch(const RecordBatch& batch) override { return Status::OK(); }
+};
+
+FlightDescriptor getDescriptor(google::protobuf::Message& command) {
+  google::protobuf::Any any;
+  any.PackFrom(command);
+
+  const std::string& string = any.SerializeAsString();
+  return FlightDescriptor::Command(string);
+}
+
+auto ReturnEmptyFlightInfo = [](const FlightCallOptions& options,
+                                const FlightDescriptor& descriptor) {
+  std::unique_ptr<FlightInfo> flight_info;
+  return flight_info;
+};
+
+TEST_F(TestFlightSqlClient, TestGetCatalogs) {
+  pb::sql::CommandGetCatalogs command;
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.GetCatalogs(call_options_));
+}
+
+TEST_F(TestFlightSqlClient, TestGetDbSchemas) {
+  std::string schema_filter_pattern = "schema_filter_pattern";
+  std::string catalog = "catalog";
+
+  pb::sql::CommandGetDbSchemas command;
+  command.set_catalog(catalog);
+  command.set_db_schema_filter_pattern(schema_filter_pattern);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.GetDbSchemas(call_options_, &catalog, &schema_filter_pattern));
+}
+
+TEST_F(TestFlightSqlClient, TestGetTables) {
+  std::string catalog = "catalog";
+  std::string schema_filter_pattern = "schema_filter_pattern";
+  std::string table_name_filter_pattern = "table_name_filter_pattern";
+  bool include_schema = true;
+  std::vector<std::string> table_types = {"type1", "type2"};
+
+  pb::sql::CommandGetTables command;
+  command.set_catalog(catalog);
+  command.set_db_schema_filter_pattern(schema_filter_pattern);
+  command.set_table_name_filter_pattern(table_name_filter_pattern);
+  command.set_include_schema(include_schema);
+  for (const std::string& table_type : table_types) {
+    command.add_table_types(table_type);
+  }
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.GetTables(call_options_, &catalog, &schema_filter_pattern,
+                                  &table_name_filter_pattern, include_schema,
+                                  &table_types));
+}
+
+TEST_F(TestFlightSqlClient, TestGetTableTypes) {
+  pb::sql::CommandGetTableTypes command;
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.GetTableTypes(call_options_));
+}
+
+TEST_F(TestFlightSqlClient, TestGetExported) {
+  std::string catalog = "catalog";
+  std::string schema = "schema";
+  std::string table = "table";
+
+  pb::sql::CommandGetExportedKeys command;
+  command.set_catalog(catalog);
+  command.set_db_schema(schema);
+  command.set_table(table);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  TableRef table_ref = {util::make_optional(catalog), util::make_optional(schema), table};
+  ASSERT_OK(sql_client_.GetExportedKeys(call_options_, table_ref));
+}
+
+TEST_F(TestFlightSqlClient, TestGetImported) {
+  std::string catalog = "catalog";
+  std::string schema = "schema";
+  std::string table = "table";
+
+  pb::sql::CommandGetImportedKeys command;
+  command.set_catalog(catalog);
+  command.set_db_schema(schema);
+  command.set_table(table);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  TableRef table_ref = {util::make_optional(catalog), util::make_optional(schema), table};
+  ASSERT_OK(sql_client_.GetImportedKeys(call_options_, table_ref));
+}
+
+TEST_F(TestFlightSqlClient, TestGetPrimary) {
+  std::string catalog = "catalog";
+  std::string schema = "schema";
+  std::string table = "table";
+
+  pb::sql::CommandGetPrimaryKeys command;
+  command.set_catalog(catalog);
+  command.set_db_schema(schema);
+  command.set_table(table);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  TableRef table_ref = {util::make_optional(catalog), util::make_optional(schema), table};
+  ASSERT_OK(sql_client_.GetPrimaryKeys(call_options_, table_ref));
+}
+
+TEST_F(TestFlightSqlClient, TestGetCrossReference) {
+  std::string pk_catalog = "pk_catalog";
+  std::string pk_schema = "pk_schema";
+  std::string pk_table = "pk_table";
+  std::string fk_catalog = "fk_catalog";
+  std::string fk_schema = "fk_schema";
+  std::string fk_table = "fk_table";
+
+  pb::sql::CommandGetCrossReference command;
+  command.set_pk_catalog(pk_catalog);
+  command.set_pk_db_schema(pk_schema);
+  command.set_pk_table(pk_table);
+  command.set_fk_catalog(fk_catalog);
+  command.set_fk_db_schema(fk_schema);
+  command.set_fk_table(fk_table);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  TableRef pk_table_ref = {util::make_optional(pk_catalog),
+                           util::make_optional(pk_schema), pk_table};
+  TableRef fk_table_ref = {util::make_optional(fk_catalog),
+                           util::make_optional(fk_schema), fk_table};
+  ASSERT_OK(sql_client_.GetCrossReference(call_options_, pk_table_ref, fk_table_ref));
+}
+
+TEST_F(TestFlightSqlClient, TestExecute) {
+  std::string query = "query";
+
+  pb::sql::CommandStatementQuery command;
+  command.set_query(query);
+  FlightDescriptor descriptor = getDescriptor(command);
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.Execute(call_options_, query));
+}
+
+TEST_F(TestFlightSqlClient, TestPreparedStatementExecute) {
+  const std::string query = "query";
+
+  ON_CALL(sql_client_, DoAction)
+      .WillByDefault([](const FlightCallOptions& options, const Action& action,
+                        std::unique_ptr<ResultStream>* results) {
+        google::protobuf::Any command;
+
+        pb::sql::ActionCreatePreparedStatementResult prepared_statement_result;
+
+        prepared_statement_result.set_prepared_statement_handle("query");
+
+        command.PackFrom(prepared_statement_result);
+
+        *results = std::unique_ptr<ResultStream>(new SimpleResultStream(
+            {Result{Buffer::FromString(command.SerializeAsString())}}));
+
+        return Status::OK();
+      });
+
+  EXPECT_CALL(sql_client_, DoAction(_, _, _)).Times(2);
+
+  ASSERT_OK_AND_ASSIGN(auto prepared_statement,
+                       sql_client_.Prepare(call_options_, query));
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(_, _));
+
+  ASSERT_OK(prepared_statement->Execute());
+}
+
+TEST_F(TestFlightSqlClient, TestPreparedStatementExecuteParameterBinding) {
+  const std::string query = "query";
+
+  ON_CALL(sql_client_, DoAction)
+      .WillByDefault([](const FlightCallOptions& options, const Action& action,
+                        std::unique_ptr<ResultStream>* results) {
+        google::protobuf::Any command;
+
+        pb::sql::ActionCreatePreparedStatementResult prepared_statement_result;
+
+        prepared_statement_result.set_prepared_statement_handle("query");
+
+        auto schema = arrow::schema({arrow::field("id", int64())});
+
+        std::shared_ptr<Buffer> schema_buffer;
+        const arrow::Result<std::shared_ptr<Buffer>>& result =
+            arrow::ipc::SerializeSchema(*schema);
+
+        ARROW_ASSIGN_OR_RAISE(schema_buffer, result);
+
+        prepared_statement_result.set_parameter_schema(schema_buffer->ToString());
+
+        command.PackFrom(prepared_statement_result);
+
+        *results = std::unique_ptr<ResultStream>(new SimpleResultStream(
+            {Result{Buffer::FromString(command.SerializeAsString())}}));
+
+        return Status::OK();
+      });
+
+  std::shared_ptr<Buffer> buffer_ptr;
+  ON_CALL(sql_client_, DoPut)
+      .WillByDefault([&buffer_ptr](const FlightCallOptions& options,
+                                   const FlightDescriptor& descriptor1,
+                                   const std::shared_ptr<Schema>& schema,
+                                   std::unique_ptr<FlightStreamWriter>* writer,
+                                   std::unique_ptr<FlightMetadataReader>* reader) {
+        writer->reset(new FlightStreamWriterMock());
+        reader->reset(new FlightMetadataReaderMock(&buffer_ptr));
+
+        return Status::OK();
+      });
+
+  EXPECT_CALL(sql_client_, DoAction(_, _, _)).Times(2);
+  EXPECT_CALL(sql_client_, DoPut(_, _, _, _, _));
+
+  ASSERT_OK_AND_ASSIGN(auto prepared_statement,
+                       sql_client_.Prepare(call_options_, query));
+
+  auto parameter_schema = prepared_statement->parameter_schema();
+
+  auto result = RecordBatchFromJSON(parameter_schema, "[[1]]");
+  ASSERT_OK(prepared_statement->SetParameters(result));
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(_, _));
+
+  ASSERT_OK(prepared_statement->Execute());
+}
+
+TEST_F(TestFlightSqlClient, TestExecuteUpdate) {
+  std::string query = "query";
+
+  pb::sql::CommandStatementUpdate command;
+
+  command.set_query(query);
+
+  google::protobuf::Any any;
+  any.PackFrom(command);
+
+  const FlightDescriptor& descriptor = FlightDescriptor::Command(any.SerializeAsString());
+
+  pb::sql::DoPutUpdateResult doPutUpdateResult;
+  doPutUpdateResult.set_record_count(100);
+  const std::string& string = doPutUpdateResult.SerializeAsString();
+
+  auto buffer_ptr = std::make_shared<Buffer>(
+      reinterpret_cast<const uint8_t*>(string.data()), doPutUpdateResult.ByteSizeLong());
+
+  ON_CALL(sql_client_, DoPut)
+      .WillByDefault([&buffer_ptr](const FlightCallOptions& options,
+                                   const FlightDescriptor& descriptor1,
+                                   const std::shared_ptr<Schema>& schema,
+                                   std::unique_ptr<FlightStreamWriter>* writer,
+                                   std::unique_ptr<FlightMetadataReader>* reader) {
+        reader->reset(new FlightMetadataReaderMock(&buffer_ptr));
+
+        return Status::OK();
+      });
+
+  std::unique_ptr<FlightInfo> flight_info;
+  std::unique_ptr<FlightStreamWriter> writer;
+  std::unique_ptr<FlightMetadataReader> reader;
+  EXPECT_CALL(sql_client_, DoPut(Ref(call_options_), descriptor, _, _, _));
+
+  ASSERT_OK_AND_ASSIGN(auto num_rows, sql_client_.ExecuteUpdate(call_options_, query));
+
+  ASSERT_EQ(num_rows, 100);
+}
+
+TEST_F(TestFlightSqlClient, TestGetSqlInfo) {
+  std::vector<int> sql_info{pb::sql::SqlInfo::FLIGHT_SQL_SERVER_NAME,
+                            pb::sql::SqlInfo::FLIGHT_SQL_SERVER_VERSION,
+                            pb::sql::SqlInfo::FLIGHT_SQL_SERVER_ARROW_VERSION};
+  pb::sql::CommandGetSqlInfo command;
+
+  for (const auto& info : sql_info) command.add_info(info);
+  google::protobuf::Any any;
+  any.PackFrom(command);
+  const FlightDescriptor& descriptor = FlightDescriptor::Command(any.SerializeAsString());
+
+  ON_CALL(sql_client_, GetFlightInfo).WillByDefault(ReturnEmptyFlightInfo);
+  EXPECT_CALL(sql_client_, GetFlightInfo(Ref(call_options_), descriptor));
+
+  ASSERT_OK(sql_client_.GetSqlInfo(call_options_, sql_info));
+}
+
+template <class Func>
+inline void AssertTestPreparedStatementExecuteUpdateOk(
+    Func func, const std::shared_ptr<Schema>* schema, FlightSqlClientMock& sql_client_) {
+  const std::string query = "SELECT * FROM IRRELEVANT";
+  int64_t expected_rows = 100L;
+  pb::sql::DoPutUpdateResult result;
+  result.set_record_count(expected_rows);
+
+  ON_CALL(sql_client_, DoAction)
+      .WillByDefault([&query, &schema](const FlightCallOptions& options,
+                                       const Action& action,
+                                       std::unique_ptr<ResultStream>* results) {
+        google::protobuf::Any command;
+        pb::sql::ActionCreatePreparedStatementResult prepared_statement_result;
+
+        prepared_statement_result.set_prepared_statement_handle(query);
+
+        if (schema != NULLPTR) {
+          std::shared_ptr<Buffer> schema_buffer;
+          const arrow::Result<std::shared_ptr<Buffer>>& result =
+              arrow::ipc::SerializeSchema(**schema);
+
+          ARROW_ASSIGN_OR_RAISE(schema_buffer, result);
+          prepared_statement_result.set_parameter_schema(schema_buffer->ToString());
+        }
+
+        command.PackFrom(prepared_statement_result);
+        *results = std::unique_ptr<ResultStream>(new SimpleResultStream(
+            {Result{Buffer::FromString(command.SerializeAsString())}}));
+
+        return Status::OK();
+      });
+  EXPECT_CALL(sql_client_, DoAction(_, _, _)).Times(2);
+
+  auto buffer = Buffer::FromString(result.SerializeAsString());
+  ON_CALL(sql_client_, DoPut)
+      .WillByDefault([&buffer](const FlightCallOptions& options,
+                               const FlightDescriptor& descriptor1,
+                               const std::shared_ptr<Schema>& schema,
+                               std::unique_ptr<FlightStreamWriter>* writer,
+                               std::unique_ptr<FlightMetadataReader>* reader) {
+        reader->reset(new FlightMetadataReaderMock(&buffer));
+        writer->reset(new FlightStreamWriterMock());
+        return Status::OK();
+      });
+  if (schema == NULLPTR) {
+    EXPECT_CALL(sql_client_, DoPut(_, _, _, _, _));
+  } else {
+    EXPECT_CALL(sql_client_, DoPut(_, _, *schema, _, _));
+  }
+
+  ASSERT_OK_AND_ASSIGN(auto prepared_statement, sql_client_.Prepare({}, query));
+  func(prepared_statement, sql_client_, schema, expected_rows);
+  ASSERT_OK_AND_ASSIGN(auto rows, prepared_statement->ExecuteUpdate());
+  ASSERT_EQ(expected_rows, rows);
+  ASSERT_OK(prepared_statement->Close());
+}
+
+TEST_F(TestFlightSqlClient, TestPreparedStatementExecuteUpdateNoParameterBinding) {
+  AssertTestPreparedStatementExecuteUpdateOk(
+      [](const std::shared_ptr<PreparedStatement>& prepared_statement,
+         FlightSqlClient& sql_client_, const std::shared_ptr<Schema>* schema,
+         const int64_t& row_count) {},
+      NULLPTR, sql_client_);
+}
+
+TEST_F(TestFlightSqlClient, TestPreparedStatementExecuteUpdateWithParameterBinding) {
+  const auto schema = arrow::schema(
+      {arrow::field("field0", arrow::utf8()), arrow::field("field1", arrow::uint8())});
+  AssertTestPreparedStatementExecuteUpdateOk(
+      [](const std::shared_ptr<PreparedStatement>& prepared_statement,
+         FlightSqlClient& sql_client_, const std::shared_ptr<Schema>* schema,
+         const int64_t& row_count) {
+        auto string_array =
+            ArrayFromJSON(utf8(), R"(["Lorem", "Ipsum", "Foo", "Bar", "Baz"])");
+        auto uint8_array = ArrayFromJSON(uint8(), R"([0, 10, 15, 20, 25])");
+        std::shared_ptr<RecordBatch> recordBatch =
+            RecordBatch::Make(*schema, row_count, {string_array, uint8_array});
+        ASSERT_OK(prepared_statement->SetParameters(recordBatch));
+      },
+      &schema, sql_client_);
+}
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_server.cc b/cpp/src/arrow/flight/sql/example/sqlite_server.cc
new file mode 100644
index 0000000..dde364f
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_server.cc
@@ -0,0 +1,813 @@
+// 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.
+
+#include "arrow/flight/sql/example/sqlite_server.h"
+
+#include <sqlite3.h>
+
+#include <boost/algorithm/string.hpp>
+#include <map>
+#include <random>
+#include <sstream>
+
+#include "arrow/api.h"
+#include "arrow/flight/sql/example/sqlite_sql_info.h"
+#include "arrow/flight/sql/example/sqlite_statement.h"
+#include "arrow/flight/sql/example/sqlite_statement_batch_reader.h"
+#include "arrow/flight/sql/example/sqlite_tables_schema_batch_reader.h"
+#include "arrow/flight/sql/server.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+namespace {
+
+/// \brief Gets a SqliteStatement by given handle
+arrow::Result<std::shared_ptr<SqliteStatement>> GetStatementByHandle(
+    const std::map<std::string, std::shared_ptr<SqliteStatement>>& prepared_statements,
+    const std::string& handle) {
+  auto search = prepared_statements.find(handle);
+  if (search == prepared_statements.end()) {
+    return Status::Invalid("Prepared statement not found");
+  }
+
+  return search->second;
+}
+
+std::string PrepareQueryForGetTables(const GetTables& command) {
+  std::stringstream table_query;
+
+  table_query << "SELECT null as catalog_name, null as schema_name, name as "
+                 "table_name, type as table_type FROM sqlite_master where 1=1";
+
+  if (command.catalog.has_value()) {
+    table_query << " and catalog_name='" << command.catalog.value() << "'";
+  }
+
+  if (command.db_schema_filter_pattern.has_value()) {
+    table_query << " and schema_name LIKE '" << command.db_schema_filter_pattern.value()
+                << "'";
+  }
+
+  if (command.table_name_filter_pattern.has_value()) {
+    table_query << " and table_name LIKE '" << command.table_name_filter_pattern.value()
+                << "'";
+  }
+
+  if (!command.table_types.empty()) {
+    table_query << " and table_type IN (";
+    size_t size = command.table_types.size();
+    for (size_t i = 0; i < size; i++) {
+      table_query << "'" << command.table_types[i] << "'";
+      if (size - 1 != i) {
+        table_query << ",";
+      }
+    }
+
+    table_query << ")";
+  }
+
+  table_query << " order by table_name";
+  return table_query.str();
+}
+
+Status SetParametersOnSQLiteStatement(sqlite3_stmt* stmt, FlightMessageReader* reader) {
+  FlightStreamChunk chunk;
+  while (true) {
+    RETURN_NOT_OK(reader->Next(&chunk));
+    std::shared_ptr<RecordBatch>& record_batch = chunk.data;
+    if (record_batch == nullptr) break;
+
+    const int64_t num_rows = record_batch->num_rows();
+    const int& num_columns = record_batch->num_columns();
+
+    for (int i = 0; i < num_rows; ++i) {
+      for (int c = 0; c < num_columns; ++c) {
+        const std::shared_ptr<Array>& column = record_batch->column(c);
+        ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Scalar> scalar, column->GetScalar(i));
+
+        auto& holder = static_cast<DenseUnionScalar&>(*scalar).value;
+
+        switch (holder->type->id()) {
+          case Type::INT64: {
+            int64_t value = static_cast<Int64Scalar&>(*holder).value;
+            sqlite3_bind_int64(stmt, c + 1, value);
+            break;
+          }
+          case Type::FLOAT: {
+            double value = static_cast<FloatScalar&>(*holder).value;
+            sqlite3_bind_double(stmt, c + 1, value);
+            break;
+          }
+          case Type::STRING: {
+            std::shared_ptr<Buffer> buffer = static_cast<StringScalar&>(*holder).value;
+            sqlite3_bind_text(stmt, c + 1, reinterpret_cast<const char*>(buffer->data()),
+                              static_cast<int>(buffer->size()), SQLITE_TRANSIENT);
+            break;
+          }
+          case Type::BINARY: {
+            std::shared_ptr<Buffer> buffer = static_cast<BinaryScalar&>(*holder).value;
+            sqlite3_bind_blob(stmt, c + 1, buffer->data(),
+                              static_cast<int>(buffer->size()), SQLITE_TRANSIENT);
+            break;
+          }
+          default:
+            return Status::Invalid("Received unsupported data type: ",
+                                   holder->type->ToString());
+        }
+      }
+    }
+  }
+
+  return Status::OK();
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> DoGetSQLiteQuery(
+    sqlite3* db, const std::string& query, const std::shared_ptr<Schema>& schema) {
+  std::shared_ptr<SqliteStatement> statement;
+
+  ARROW_ASSIGN_OR_RAISE(statement, SqliteStatement::Create(db, query));
+
+  std::shared_ptr<SqliteStatementBatchReader> reader;
+  ARROW_ASSIGN_OR_RAISE(reader, SqliteStatementBatchReader::Create(statement, schema));
+
+  return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoForCommand(
+    const FlightDescriptor& descriptor, const std::shared_ptr<Schema>& schema) {
+  std::vector<FlightEndpoint> endpoints{FlightEndpoint{{descriptor.cmd}, {}}};
+  ARROW_ASSIGN_OR_RAISE(auto result,
+                        FlightInfo::Make(*schema, descriptor, endpoints, -1, -1))
+
+  return std::unique_ptr<FlightInfo>(new FlightInfo(result));
+}
+
+std::string PrepareQueryForGetImportedOrExportedKeys(const std::string& filter) {
+  return R"(SELECT * FROM (SELECT NULL AS pk_catalog_name,
+    NULL AS pk_schema_name,
+    p."table" AS pk_table_name,
+    p."to" AS pk_column_name,
+    NULL AS fk_catalog_name,
+    NULL AS fk_schema_name,
+    m.name AS fk_table_name,
+    p."from" AS fk_column_name,
+    p.seq AS key_sequence,
+    NULL AS pk_key_name,
+    NULL AS fk_key_name,
+    CASE
+        WHEN p.on_update = 'CASCADE' THEN 0
+        WHEN p.on_update = 'RESTRICT' THEN 1
+        WHEN p.on_update = 'SET NULL' THEN 2
+        WHEN p.on_update = 'NO ACTION' THEN 3
+        WHEN p.on_update = 'SET DEFAULT' THEN 4
+    END AS update_rule,
+    CASE
+        WHEN p.on_delete = 'CASCADE' THEN 0
+        WHEN p.on_delete = 'RESTRICT' THEN 1
+        WHEN p.on_delete = 'SET NULL' THEN 2
+        WHEN p.on_delete = 'NO ACTION' THEN 3
+        WHEN p.on_delete = 'SET DEFAULT' THEN 4
+    END AS delete_rule
+  FROM sqlite_master m
+  JOIN pragma_foreign_key_list(m.name) p ON m.name != p."table"
+  WHERE m.type = 'table') WHERE )" +
+         filter + R"( ORDER BY
+  pk_catalog_name, pk_schema_name, pk_table_name, pk_key_name, key_sequence)";
+}
+
+}  // namespace
+
+std::shared_ptr<DataType> GetArrowType(const char* sqlite_type) {
+  if (sqlite_type == NULLPTR) {
+    // SQLite may not know the column type yet.
+    return null();
+  }
+
+  if (boost::iequals(sqlite_type, "int") || boost::iequals(sqlite_type, "integer")) {
+    return int64();
+  } else if (boost::iequals(sqlite_type, "REAL")) {
+    return float64();
+  } else if (boost::iequals(sqlite_type, "BLOB")) {
+    return binary();
+  } else if (boost::iequals(sqlite_type, "TEXT") ||
+             boost::istarts_with(sqlite_type, "char") ||
+             boost::istarts_with(sqlite_type, "varchar")) {
+    return utf8();
+  } else {
+    throw std::invalid_argument("Invalid SQLite type: " + std::string(sqlite_type));
+  }
+}
+
+class SQLiteFlightSqlServer::Impl {
+  sqlite3* db_;
+  std::map<std::string, std::shared_ptr<SqliteStatement>> prepared_statements_;
+  std::default_random_engine gen_;
+
+ public:
+  explicit Impl(sqlite3* db) : db_(db) {}
+
+  ~Impl() { sqlite3_close(db_); }
+
+  std::string GenerateRandomString() {
+    uint32_t length = 16;
+
+    std::uniform_int_distribution<char> dist('0', 'z');
+    std::string ret(length, 0);
+    auto get_random_char = [&]() { return dist(gen_); };
+    std::generate_n(ret.begin(), length, get_random_char);
+    return ret;
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoStatement(
+      const ServerCallContext& context, const StatementQuery& command,
+      const FlightDescriptor& descriptor) {
+    const std::string& query = command.query;
+
+    ARROW_ASSIGN_OR_RAISE(auto statement, SqliteStatement::Create(db_, query));
+
+    ARROW_ASSIGN_OR_RAISE(auto schema, statement->GetSchema());
+
+    ARROW_ASSIGN_OR_RAISE(auto ticket_string, CreateStatementQueryTicket(query));
+    std::vector<FlightEndpoint> endpoints{FlightEndpoint{{ticket_string}, {}}};
+    ARROW_ASSIGN_OR_RAISE(auto result,
+                          FlightInfo::Make(*schema, descriptor, endpoints, -1, -1))
+
+    return std::unique_ptr<FlightInfo>(new FlightInfo(result));
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetStatement(
+      const ServerCallContext& context, const StatementQueryTicket& command) {
+    const std::string& sql = command.statement_handle;
+
+    std::shared_ptr<SqliteStatement> statement;
+    ARROW_ASSIGN_OR_RAISE(statement, SqliteStatement::Create(db_, sql));
+
+    std::shared_ptr<SqliteStatementBatchReader> reader;
+    ARROW_ASSIGN_OR_RAISE(reader, SqliteStatementBatchReader::Create(statement));
+
+    return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCatalogs(
+      const ServerCallContext& context, const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetCatalogsSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCatalogs(
+      const ServerCallContext& context) {
+    // As SQLite doesn't support catalogs, this will return an empty record batch.
+
+    const std::shared_ptr<Schema>& schema = SqlSchema::GetCatalogsSchema();
+
+    StringBuilder catalog_name_builder;
+    ARROW_ASSIGN_OR_RAISE(auto catalog_name, catalog_name_builder.Finish());
+
+    const std::shared_ptr<RecordBatch>& batch =
+        RecordBatch::Make(schema, 0, {catalog_name});
+
+    ARROW_ASSIGN_OR_RAISE(auto reader, RecordBatchReader::Make({batch}));
+
+    return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command,
+      const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetDbSchemasSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetDbSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command) {
+    // As SQLite doesn't support schemas, this will return an empty record batch.
+
+    const std::shared_ptr<Schema>& schema = SqlSchema::GetDbSchemasSchema();
+
+    StringBuilder catalog_name_builder;
+    ARROW_ASSIGN_OR_RAISE(auto catalog_name, catalog_name_builder.Finish());
+    StringBuilder schema_name_builder;
+    ARROW_ASSIGN_OR_RAISE(auto schema_name, schema_name_builder.Finish());
+
+    const std::shared_ptr<RecordBatch>& batch =
+        RecordBatch::Make(schema, 0, {catalog_name, schema_name});
+
+    ARROW_ASSIGN_OR_RAISE(auto reader, RecordBatchReader::Make({batch}));
+
+    return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTables(
+      const ServerCallContext& context, const GetTables& command,
+      const FlightDescriptor& descriptor) {
+    std::vector<FlightEndpoint> endpoints{FlightEndpoint{{descriptor.cmd}, {}}};
+
+    bool include_schema = command.include_schema;
+
+    ARROW_ASSIGN_OR_RAISE(
+        auto result,
+        FlightInfo::Make(include_schema ? *SqlSchema::GetTablesSchemaWithIncludedSchema()
+                                        : *SqlSchema::GetTablesSchema(),
+                         descriptor, endpoints, -1, -1))
+
+    return std::unique_ptr<FlightInfo>(new FlightInfo(result));
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTables(
+      const ServerCallContext& context, const GetTables& command) {
+    std::string query = PrepareQueryForGetTables(command);
+
+    std::shared_ptr<SqliteStatement> statement;
+    ARROW_ASSIGN_OR_RAISE(statement, SqliteStatement::Create(db_, query));
+
+    std::shared_ptr<SqliteStatementBatchReader> reader;
+    ARROW_ASSIGN_OR_RAISE(reader, SqliteStatementBatchReader::Create(
+                                      statement, SqlSchema::GetTablesSchema()));
+
+    if (command.include_schema) {
+      std::shared_ptr<SqliteTablesWithSchemaBatchReader> table_schema_reader =
+          std::make_shared<SqliteTablesWithSchemaBatchReader>(reader, query, db_);
+      return std::unique_ptr<FlightDataStream>(
+          new RecordBatchStream(table_schema_reader));
+    } else {
+      return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+    }
+  }
+
+  arrow::Result<int64_t> DoPutCommandStatementUpdate(const ServerCallContext& context,
+                                                     const StatementUpdate& command) {
+    const std::string& sql = command.query;
+
+    ARROW_ASSIGN_OR_RAISE(auto statement, SqliteStatement::Create(db_, sql));
+
+    return statement->ExecuteUpdate();
+  }
+
+  arrow::Result<ActionCreatePreparedStatementResult> CreatePreparedStatement(
+      const ServerCallContext& context,
+      const ActionCreatePreparedStatementRequest& request) {
+    std::shared_ptr<SqliteStatement> statement;
+    ARROW_ASSIGN_OR_RAISE(statement, SqliteStatement::Create(db_, request.query));
+    const std::string handle = GenerateRandomString();
+    prepared_statements_[handle] = statement;
+
+    ARROW_ASSIGN_OR_RAISE(auto dataset_schema, statement->GetSchema());
+
+    sqlite3_stmt* stmt = statement->GetSqlite3Stmt();
+    const int parameter_count = sqlite3_bind_parameter_count(stmt);
+    std::vector<std::shared_ptr<arrow::Field>> parameter_fields;
+    parameter_fields.reserve(parameter_count);
+
+    // As SQLite doesn't know the parameter types before executing the query, the
+    // example server is accepting any SQLite supported type as input by using a dense
+    // union.
+    const std::shared_ptr<DataType>& dense_union_type = GetUnknownColumnDataType();
+
+    for (int i = 0; i < parameter_count; i++) {
+      const char* parameter_name_chars = sqlite3_bind_parameter_name(stmt, i + 1);
+      std::string parameter_name;
+      if (parameter_name_chars == NULLPTR) {
+        parameter_name = std::string("parameter_") + std::to_string(i + 1);
+      } else {
+        parameter_name = parameter_name_chars;
+      }
+      parameter_fields.push_back(field(parameter_name, dense_union_type));
+    }
+
+    const std::shared_ptr<Schema>& parameter_schema = arrow::schema(parameter_fields);
+
+    ActionCreatePreparedStatementResult result{.dataset_schema = dataset_schema,
+                                               .parameter_schema = parameter_schema,
+                                               .prepared_statement_handle = handle};
+
+    return result;
+  }
+
+  Status ClosePreparedStatement(const ServerCallContext& context,
+                                const ActionClosePreparedStatementRequest& request) {
+    const std::string& prepared_statement_handle = request.prepared_statement_handle;
+
+    auto search = prepared_statements_.find(prepared_statement_handle);
+    if (search != prepared_statements_.end()) {
+      prepared_statements_.erase(prepared_statement_handle);
+    } else {
+      return Status::Invalid("Prepared statement not found");
+    }
+
+    return Status::OK();
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command,
+      const FlightDescriptor& descriptor) {
+    const std::string& prepared_statement_handle = command.prepared_statement_handle;
+
+    auto search = prepared_statements_.find(prepared_statement_handle);
+    if (search == prepared_statements_.end()) {
+      return Status::Invalid("Prepared statement not found");
+    }
+
+    std::shared_ptr<SqliteStatement> statement = search->second;
+
+    ARROW_ASSIGN_OR_RAISE(auto schema, statement->GetSchema());
+
+    return GetFlightInfoForCommand(descriptor, schema);
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command) {
+    const std::string& prepared_statement_handle = command.prepared_statement_handle;
+
+    auto search = prepared_statements_.find(prepared_statement_handle);
+    if (search == prepared_statements_.end()) {
+      return Status::Invalid("Prepared statement not found");
+    }
+
+    std::shared_ptr<SqliteStatement> statement = search->second;
+
+    std::shared_ptr<SqliteStatementBatchReader> reader;
+    ARROW_ASSIGN_OR_RAISE(reader, SqliteStatementBatchReader::Create(statement));
+
+    return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+  }
+
+  Status DoPutPreparedStatementQuery(const ServerCallContext& context,
+                                     const PreparedStatementQuery& command,
+                                     FlightMessageReader* reader,
+                                     FlightMetadataWriter* writer) {
+    const std::string& prepared_statement_handle = command.prepared_statement_handle;
+    ARROW_ASSIGN_OR_RAISE(
+        auto statement,
+        GetStatementByHandle(prepared_statements_, prepared_statement_handle));
+
+    sqlite3_stmt* stmt = statement->GetSqlite3Stmt();
+    ARROW_RETURN_NOT_OK(SetParametersOnSQLiteStatement(stmt, reader));
+
+    return Status::OK();
+  }
+
+  arrow::Result<int64_t> DoPutPreparedStatementUpdate(
+      const ServerCallContext& context, const PreparedStatementUpdate& command,
+      FlightMessageReader* reader) {
+    const std::string& prepared_statement_handle = command.prepared_statement_handle;
+    ARROW_ASSIGN_OR_RAISE(
+        auto statement,
+        GetStatementByHandle(prepared_statements_, prepared_statement_handle));
+
+    sqlite3_stmt* stmt = statement->GetSqlite3Stmt();
+    ARROW_RETURN_NOT_OK(SetParametersOnSQLiteStatement(stmt, reader));
+
+    return statement->ExecuteUpdate();
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTableTypes(
+      const ServerCallContext& context, const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetTableTypesSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTableTypes(
+      const ServerCallContext& context) {
+    std::string query = "SELECT DISTINCT type as table_type FROM sqlite_master";
+
+    return DoGetSQLiteQuery(db_, query, SqlSchema::GetTableTypesSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command,
+      const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetPrimaryKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command) {
+    std::stringstream table_query;
+
+    // The field key_name can not be recovered by the sqlite, so it is being set
+    // to null following the same pattern for catalog_name and schema_name.
+    table_query << "SELECT null as catalog_name, null as schema_name, table_name, "
+                   "name as column_name,  pk as key_sequence, null as key_name\n"
+                   "FROM pragma_table_info(table_name)\n"
+                   "    JOIN (SELECT null as catalog_name, null as schema_name, name as "
+                   "table_name, type as table_type\n"
+                   "FROM sqlite_master) where 1=1 and pk != 0";
+
+    const TableRef& table_ref = command.table_ref;
+    if (table_ref.catalog.has_value()) {
+      table_query << " and catalog_name LIKE '" << table_ref.catalog.value() << "'";
+    }
+
+    if (table_ref.db_schema.has_value()) {
+      table_query << " and schema_name LIKE '" << table_ref.db_schema.value() << "'";
+    }
+
+    table_query << " and table_name LIKE '" << table_ref.table << "'";
+
+    return DoGetSQLiteQuery(db_, table_query.str(), SqlSchema::GetPrimaryKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command,
+      const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetImportedKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command) {
+    const TableRef& table_ref = command.table_ref;
+    std::string filter = "fk_table_name = '" + table_ref.table + "'";
+    if (table_ref.catalog.has_value()) {
+      filter += " AND fk_catalog_name = '" + table_ref.catalog.value() + "'";
+    }
+    if (table_ref.db_schema.has_value()) {
+      filter += " AND fk_schema_name = '" + table_ref.db_schema.value() + "'";
+    }
+    std::string query = PrepareQueryForGetImportedOrExportedKeys(filter);
+
+    return DoGetSQLiteQuery(db_, query, SqlSchema::GetImportedKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command,
+      const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetExportedKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command) {
+    const TableRef& table_ref = command.table_ref;
+    std::string filter = "pk_table_name = '" + table_ref.table + "'";
+    if (table_ref.catalog.has_value()) {
+      filter += " AND pk_catalog_name = '" + table_ref.catalog.value() + "'";
+    }
+    if (table_ref.db_schema.has_value()) {
+      filter += " AND pk_schema_name = '" + table_ref.db_schema.value() + "'";
+    }
+    std::string query = PrepareQueryForGetImportedOrExportedKeys(filter);
+
+    return DoGetSQLiteQuery(db_, query, SqlSchema::GetExportedKeysSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command,
+      const FlightDescriptor& descriptor) {
+    return GetFlightInfoForCommand(descriptor, SqlSchema::GetCrossReferenceSchema());
+  }
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command) {
+    const TableRef& pk_table_ref = command.pk_table_ref;
+    std::string filter = "pk_table_name = '" + pk_table_ref.table + "'";
+    if (pk_table_ref.catalog.has_value()) {
+      filter += " AND pk_catalog_name = '" + pk_table_ref.catalog.value() + "'";
+    }
+    if (pk_table_ref.db_schema.has_value()) {
+      filter += " AND pk_schema_name = '" + pk_table_ref.db_schema.value() + "'";
+    }
+
+    const TableRef& fk_table_ref = command.fk_table_ref;
+    filter += " AND fk_table_name = '" + fk_table_ref.table + "'";
+    if (fk_table_ref.catalog.has_value()) {
+      filter += " AND fk_catalog_name = '" + fk_table_ref.catalog.value() + "'";
+    }
+    if (fk_table_ref.db_schema.has_value()) {
+      filter += " AND fk_schema_name = '" + fk_table_ref.db_schema.value() + "'";
+    }
+    std::string query = PrepareQueryForGetImportedOrExportedKeys(filter);
+
+    return DoGetSQLiteQuery(db_, query, SqlSchema::GetCrossReferenceSchema());
+  }
+
+  Status ExecuteSql(const std::string& sql) {
+    char* err_msg = nullptr;
+    int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err_msg);
+    if (rc != SQLITE_OK) {
+      std::string error_msg;
+      if (err_msg != nullptr) {
+        error_msg = err_msg;
+      }
+      sqlite3_free(err_msg);
+      return Status::ExecutionError(error_msg);
+    }
+    return Status::OK();
+  }
+};
+
+SQLiteFlightSqlServer::SQLiteFlightSqlServer(std::shared_ptr<Impl> impl)
+    : impl_(std::move(impl)) {}
+
+arrow::Result<std::shared_ptr<SQLiteFlightSqlServer>> SQLiteFlightSqlServer::Create() {
+  sqlite3* db = nullptr;
+
+  if (sqlite3_open(":memory:", &db)) {
+    std::string err_msg = "Can't open database: ";
+    if (db != nullptr) {
+      err_msg += sqlite3_errmsg(db);
+      sqlite3_close(db);
+    } else {
+      err_msg += "Unable to start SQLite. Insufficient memory";
+    }
+
+    return Status::Invalid(err_msg);
+  }
+
+  std::shared_ptr<Impl> impl = std::make_shared<Impl>(db);
+
+  std::shared_ptr<SQLiteFlightSqlServer> result(new SQLiteFlightSqlServer(impl));
+  for (const auto& id_to_result : GetSqlInfoResultMap()) {
+    result->RegisterSqlInfo(id_to_result.first, id_to_result.second);
+  }
+
+  ARROW_RETURN_NOT_OK(result->ExecuteSql(R"(
+    CREATE TABLE foreignTable (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    foreignName varchar(100),
+    value int);
+
+    CREATE TABLE intTable (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    keyName varchar(100),
+    value int,
+    foreignId int references foreignTable(id));
+
+    INSERT INTO foreignTable (foreignName, value) VALUES ('keyOne', 1);
+    INSERT INTO foreignTable (foreignName, value) VALUES ('keyTwo', 0);
+    INSERT INTO foreignTable (foreignName, value) VALUES ('keyThree', -1);
+    INSERT INTO intTable (keyName, value, foreignId) VALUES ('one', 1, 1);
+    INSERT INTO intTable (keyName, value, foreignId) VALUES ('zero', 0, 1);
+    INSERT INTO intTable (keyName, value, foreignId) VALUES ('negative one', -1, 1);
+    INSERT INTO intTable (keyName, value, foreignId) VALUES (NULL, NULL, NULL);
+  )"));
+
+  return result;
+}
+
+SQLiteFlightSqlServer::~SQLiteFlightSqlServer() = default;
+
+Status SQLiteFlightSqlServer::ExecuteSql(const std::string& sql) {
+  return impl_->ExecuteSql(sql);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> SQLiteFlightSqlServer::GetFlightInfoStatement(
+    const ServerCallContext& context, const StatementQuery& command,
+    const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoStatement(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetStatement(
+    const ServerCallContext& context, const StatementQueryTicket& command) {
+  return impl_->DoGetStatement(context, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> SQLiteFlightSqlServer::GetFlightInfoCatalogs(
+    const ServerCallContext& context, const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoCatalogs(context, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetCatalogs(
+    const ServerCallContext& context) {
+  return impl_->DoGetCatalogs(context);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> SQLiteFlightSqlServer::GetFlightInfoSchemas(
+    const ServerCallContext& context, const GetDbSchemas& command,
+    const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoSchemas(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetDbSchemas(
+    const ServerCallContext& context, const GetDbSchemas& command) {
+  return impl_->DoGetDbSchemas(context, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> SQLiteFlightSqlServer::GetFlightInfoTables(
+    const ServerCallContext& context, const GetTables& command,
+    const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoTables(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetTables(
+    const ServerCallContext& context, const GetTables& command) {
+  return impl_->DoGetTables(context, command);
+}
+
+arrow::Result<int64_t> SQLiteFlightSqlServer::DoPutCommandStatementUpdate(
+    const ServerCallContext& context, const StatementUpdate& command) {
+  return impl_->DoPutCommandStatementUpdate(context, command);
+}
+
+arrow::Result<ActionCreatePreparedStatementResult>
+SQLiteFlightSqlServer::CreatePreparedStatement(
+    const ServerCallContext& context,
+    const ActionCreatePreparedStatementRequest& request) {
+  return impl_->CreatePreparedStatement(context, request);
+}
+
+Status SQLiteFlightSqlServer::ClosePreparedStatement(
+    const ServerCallContext& context,
+    const ActionClosePreparedStatementRequest& request) {
+  return impl_->ClosePreparedStatement(context, request);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+SQLiteFlightSqlServer::GetFlightInfoPreparedStatement(
+    const ServerCallContext& context, const PreparedStatementQuery& command,
+    const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoPreparedStatement(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>>
+SQLiteFlightSqlServer::DoGetPreparedStatement(const ServerCallContext& context,
+                                              const PreparedStatementQuery& command) {
+  return impl_->DoGetPreparedStatement(context, command);
+}
+
+Status SQLiteFlightSqlServer::DoPutPreparedStatementQuery(
+    const ServerCallContext& context, const PreparedStatementQuery& command,
+    FlightMessageReader* reader, FlightMetadataWriter* writer) {
+  return impl_->DoPutPreparedStatementQuery(context, command, reader, writer);
+}
+
+arrow::Result<int64_t> SQLiteFlightSqlServer::DoPutPreparedStatementUpdate(
+    const ServerCallContext& context, const PreparedStatementUpdate& command,
+    FlightMessageReader* reader) {
+  return impl_->DoPutPreparedStatementUpdate(context, command, reader);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> SQLiteFlightSqlServer::GetFlightInfoTableTypes(
+    const ServerCallContext& context, const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoTableTypes(context, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetTableTypes(
+    const ServerCallContext& context) {
+  return impl_->DoGetTableTypes(context);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+SQLiteFlightSqlServer::GetFlightInfoPrimaryKeys(const ServerCallContext& context,
+                                                const GetPrimaryKeys& command,
+                                                const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoPrimaryKeys(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetPrimaryKeys(
+    const ServerCallContext& context, const GetPrimaryKeys& command) {
+  return impl_->DoGetPrimaryKeys(context, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+SQLiteFlightSqlServer::GetFlightInfoImportedKeys(const ServerCallContext& context,
+                                                 const GetImportedKeys& command,
+                                                 const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoImportedKeys(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetImportedKeys(
+    const ServerCallContext& context, const GetImportedKeys& command) {
+  return impl_->DoGetImportedKeys(context, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+SQLiteFlightSqlServer::GetFlightInfoExportedKeys(const ServerCallContext& context,
+                                                 const GetExportedKeys& command,
+                                                 const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoExportedKeys(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> SQLiteFlightSqlServer::DoGetExportedKeys(
+    const ServerCallContext& context, const GetExportedKeys& command) {
+  return impl_->DoGetExportedKeys(context, command);
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+SQLiteFlightSqlServer::GetFlightInfoCrossReference(const ServerCallContext& context,
+                                                   const GetCrossReference& command,
+                                                   const FlightDescriptor& descriptor) {
+  return impl_->GetFlightInfoCrossReference(context, command, descriptor);
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>>
+SQLiteFlightSqlServer::DoGetCrossReference(const ServerCallContext& context,
+                                           const GetCrossReference& command) {
+  return impl_->DoGetCrossReference(context, command);
+}
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_server.h b/cpp/src/arrow/flight/sql/example/sqlite_server.h
new file mode 100644
index 0000000..b2954b8
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_server.h
@@ -0,0 +1,142 @@
+// 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.
+
+#pragma once
+
+#include <sqlite3.h>
+
+#include <memory>
+#include <string>
+
+#include "arrow/api.h"
+#include "arrow/flight/sql/example/sqlite_statement.h"
+#include "arrow/flight/sql/example/sqlite_statement_batch_reader.h"
+#include "arrow/flight/sql/server.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+/// \brief Convert a column type to a ArrowType.
+/// \param sqlite_type the sqlite type.
+/// \return            The equivalent ArrowType.
+std::shared_ptr<DataType> GetArrowType(const char* sqlite_type);
+
+/// \brief  Get the DataType used when parameter type is not known.
+/// \return DataType used when parameter type is not known.
+inline std::shared_ptr<DataType> GetUnknownColumnDataType() {
+  return dense_union({
+      field("string", utf8()),
+      field("bytes", binary()),
+      field("bigint", int64()),
+      field("double", float64()),
+  });
+}
+
+/// \brief Example implementation of FlightSqlServerBase backed by an in-memory SQLite3
+///        database.
+class SQLiteFlightSqlServer : public FlightSqlServerBase {
+ public:
+  ~SQLiteFlightSqlServer() override;
+
+  static arrow::Result<std::shared_ptr<SQLiteFlightSqlServer>> Create();
+
+  /// \brief Auxiliary method used to execute an arbitrary SQL statement on the underlying
+  ///        SQLite database.
+  Status ExecuteSql(const std::string& sql);
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoStatement(
+      const ServerCallContext& context, const StatementQuery& command,
+      const FlightDescriptor& descriptor) override;
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetStatement(
+      const ServerCallContext& context, const StatementQueryTicket& command) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCatalogs(
+      const ServerCallContext& context, const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCatalogs(
+      const ServerCallContext& context) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command,
+      const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetDbSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command) override;
+  arrow::Result<int64_t> DoPutCommandStatementUpdate(
+      const ServerCallContext& context, const StatementUpdate& update) override;
+  arrow::Result<ActionCreatePreparedStatementResult> CreatePreparedStatement(
+      const ServerCallContext& context,
+      const ActionCreatePreparedStatementRequest& request) override;
+  Status ClosePreparedStatement(
+      const ServerCallContext& context,
+      const ActionClosePreparedStatementRequest& request) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command,
+      const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command) override;
+  Status DoPutPreparedStatementQuery(const ServerCallContext& context,
+                                     const PreparedStatementQuery& command,
+                                     FlightMessageReader* reader,
+                                     FlightMetadataWriter* writer) override;
+  arrow::Result<int64_t> DoPutPreparedStatementUpdate(
+      const ServerCallContext& context, const PreparedStatementUpdate& command,
+      FlightMessageReader* reader) override;
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTables(
+      const ServerCallContext& context, const GetTables& command,
+      const FlightDescriptor& descriptor) override;
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTables(
+      const ServerCallContext& context, const GetTables& command) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTableTypes(
+      const ServerCallContext& context, const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTableTypes(
+      const ServerCallContext& context) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command,
+      const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command,
+      const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command) override;
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command,
+      const FlightDescriptor& descriptor) override;
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command) override;
+
+  arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command,
+      const FlightDescriptor& descriptor) override;
+
+  arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command) override;
+
+ private:
+  class Impl;
+  std::shared_ptr<Impl> impl_;
+
+  explicit SQLiteFlightSqlServer(std::shared_ptr<Impl> impl);
+};
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_sql_info.cc b/cpp/src/arrow/flight/sql/example/sqlite_sql_info.cc
new file mode 100644
index 0000000..94f25b3
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_sql_info.cc
@@ -0,0 +1,223 @@
+// 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.
+
+#include "arrow/flight/sql/example/sqlite_sql_info.h"
+
+#include "arrow/flight/sql/types.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+/// \brief Gets the mapping from SQL info ids to SqlInfoResult instances.
+/// \return the cache.
+SqlInfoResultMap GetSqlInfoResultMap() {
+  return {
+      {SqlInfoOptions::SqlInfo::FLIGHT_SQL_SERVER_NAME,
+       SqlInfoResult(std::string("db_name"))},
+      {SqlInfoOptions::SqlInfo::FLIGHT_SQL_SERVER_VERSION,
+       SqlInfoResult(std::string("sqlite 3"))},
+      {SqlInfoOptions::SqlInfo::FLIGHT_SQL_SERVER_ARROW_VERSION,
+       SqlInfoResult(std::string("7.0.0-SNAPSHOT" /* Only an example */))},
+      {SqlInfoOptions::SqlInfo::FLIGHT_SQL_SERVER_READ_ONLY, SqlInfoResult(false)},
+      {SqlInfoOptions::SqlInfo::SQL_DDL_CATALOG,
+       SqlInfoResult(false /* SQLite 3 does not support catalogs */)},
+      {SqlInfoOptions::SqlInfo::SQL_DDL_SCHEMA,
+       SqlInfoResult(false /* SQLite 3 does not support schemas */)},
+      {SqlInfoOptions::SqlInfo::SQL_DDL_TABLE, SqlInfoResult(true)},
+      {SqlInfoOptions::SqlInfo::SQL_IDENTIFIER_CASE,
+       SqlInfoResult(int64_t(SqlInfoOptions::SqlSupportedCaseSensitivity::
+                                 SQL_CASE_SENSITIVITY_CASE_INSENSITIVE))},
+      {SqlInfoOptions::SqlInfo::SQL_IDENTIFIER_QUOTE_CHAR,
+       SqlInfoResult(std::string("\""))},
+      {SqlInfoOptions::SqlInfo::SQL_QUOTED_IDENTIFIER_CASE,
+       SqlInfoResult(int64_t(SqlInfoOptions::SqlSupportedCaseSensitivity::
+                                 SQL_CASE_SENSITIVITY_CASE_INSENSITIVE))},
+      {SqlInfoOptions::SqlInfo::SQL_ALL_TABLES_ARE_SELECTABLE, SqlInfoResult(true)},
+      {SqlInfoOptions::SqlInfo::SQL_NULL_ORDERING,
+       SqlInfoResult(
+           int64_t(SqlInfoOptions::SqlNullOrdering::SQL_NULLS_SORTED_AT_START))},
+      {SqlInfoOptions::SqlInfo::SQL_KEYWORDS,
+       SqlInfoResult(std::vector<std::string>({"ABORT",
+                                               "ACTION",
+                                               "ADD",
+                                               "AFTER",
+                                               "ALL",
+                                               "ALTER",
+                                               "ALWAYS",
+                                               "ANALYZE",
+                                               "AND",
+                                               "AS",
+                                               "ASC",
+                                               "ATTACH",
+                                               "AUTOINCREMENT",
+                                               "BEFORE",
+                                               "BEGIN",
+                                               "BETWEEN",
+                                               "BY",
+                                               "CASCADE",
+                                               "CASE",
+                                               "CAST",
+                                               "CHECK",
+                                               "COLLATE",
+                                               "COLUMN",
+                                               "COMMIT",
+                                               "CONFLICT",
+                                               "CONSTRAINT",
+                                               "CREATE",
+                                               "CROSS",
+                                               "CURRENT",
+                                               "CURRENT_DATE",
+                                               "CURRENT_TIME",
+                                               "CURRENT_TIMESTAMP",
+                                               "DATABASE",
+                                               "DEFAULT",
+                                               "DEFERRABLE",
+                                               "DEFERRED",
+                                               "DELETE",
+                                               "DESC",
+                                               "DETACH",
+                                               "DISTINCT",
+                                               "DO",
+                                               "DROP",
+                                               "EACH",
+                                               "ELSE",
+                                               "END",
+                                               "ESCAPE",
+                                               "EXCEPT",
+                                               "EXCLUDE",
+                                               "EXCLUSIVE",
+                                               "EXISTS",
+                                               "EXPLAIN",
+                                               "FAIL",
+                                               "FILTER",
+                                               "FIRST",
+                                               "FOLLOWING",
+                                               "FOR",
+                                               "FOREIGN",
+                                               "FROM",
+                                               "FULL",
+                                               "GENERATED",
+                                               "GLOB",
+                                               "GROUP",
+                                               "GROUPS",
+                                               "HAVING",
+                                               "IF",
+                                               "IGNORE",
+                                               "IMMEDIATE",
+                                               "IN",
+                                               "INDEX",
+                                               "INDEXED",
+                                               "INITIALLY",
+                                               "INNER",
+                                               "INSERT",
+                                               "INSTEAD",
+                                               "INTERSECT",
+                                               "INTO",
+                                               "IS",
+                                               "ISNULL",
+                                               "JOIN",
+                                               "KEY",
+                                               "LAST",
+                                               "LEFT",
+                                               "LIKE",
+                                               "LIMIT",
+                                               "MATCH",
+                                               "MATERIALIZED",
+                                               "NATURAL",
+                                               "NO",
+                                               "NOT",
+                                               "NOTHING",
+                                               "NOTNULL",
+                                               "NULL",
+                                               "NULLS",
+                                               "OF",
+                                               "OFFSET",
+                                               "ON",
+                                               "OR",
+                                               "ORDER",
+                                               "OTHERS",
+                                               "OUTER",
+                                               "OVER",
+                                               "PARTITION",
+                                               "PLAN",
+                                               "PRAGMA",
+                                               "PRECEDING",
+                                               "PRIMARY",
+                                               "QUERY",
+                                               "RAISE",
+                                               "RANGE",
+                                               "RECURSIVE",
+                                               "REFERENCES",
+                                               "REGEXP",
+                                               "REINDEX",
+                                               "RELEASE",
+                                               "RENAME",
+                                               "REPLACE",
+                                               "RESTRICT",
+                                               "RETURNING",
+                                               "RIGHT",
+                                               "ROLLBACK",
+                                               "ROW",
+                                               "ROWS",
+                                               "SAVEPOINT",
+                                               "SELECT",
+                                               "SET",
+                                               "TABLE",
+                                               "TEMP",
+                                               "TEMPORARY",
+                                               "THEN",
+                                               "TIES",
+                                               "TO",
+                                               "TRANSACTION",
+                                               "TRIGGER",
+                                               "UNBOUNDED",
+                                               "UNION",
+                                               "UNIQUE",
+                                               "UPDATE",
+                                               "USING",
+                                               "VACUUM",
+                                               "VALUES",
+                                               "VIEW",
+                                               "VIRTUAL",
+                                               "WHEN",
+                                               "WHERE",
+                                               "WINDOW",
+                                               "WITH",
+                                               "WITHOUT"}))},
+      {SqlInfoOptions::SqlInfo::SQL_NUMERIC_FUNCTIONS,
+       SqlInfoResult(std::vector<std::string>(
+           {"ACOS",    "ACOSH", "ASIN", "ASINH",   "ATAN", "ATAN2", "ATANH", "CEIL",
+            "CEILING", "COS",   "COSH", "DEGREES", "EXP",  "FLOOR", "LN",    "LOG",
+            "LOG",     "LOG10", "LOG2", "MOD",     "PI",   "POW",   "POWER", "RADIANS",
+            "SIN",     "SINH",  "SQRT", "TAN",     "TANH", "TRUNC"}))},
+      {SqlInfoOptions::SqlInfo::SQL_STRING_FUNCTIONS,
+       SqlInfoResult(
+           std::vector<std::string>({"SUBSTR", "TRIM", "LTRIM", "RTRIM", "LENGTH",
+                                     "REPLACE", "UPPER", "LOWER", "INSTR"}))},
+      {SqlInfoOptions::SqlInfo::SQL_SUPPORTS_CONVERT,
+       SqlInfoResult(std::unordered_map<int32_t, std::vector<int32_t>>(
+           {{SqlInfoOptions::SqlSupportsConvert::SQL_CONVERT_BIGINT,
+             std::vector<int32_t>(
+                 {SqlInfoOptions::SqlSupportsConvert::SQL_CONVERT_INTEGER})}}))}};
+}
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_sql_info.h b/cpp/src/arrow/flight/sql/example/sqlite_sql_info.h
new file mode 100644
index 0000000..3c6dd42
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_sql_info.h
@@ -0,0 +1,34 @@
+// 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.
+
+#pragma once
+
+#include "arrow/flight/sql/types.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+/// \brief Gets the mapping from SQL info ids to SqlInfoResult instances.
+/// \return the cache.
+SqlInfoResultMap GetSqlInfoResultMap();
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_statement.cc b/cpp/src/arrow/flight/sql/example/sqlite_statement.cc
new file mode 100644
index 0000000..018f8de
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_statement.cc
@@ -0,0 +1,137 @@
+// 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.
+
+#include "arrow/flight/sql/example/sqlite_statement.h"
+
+#include <sqlite3.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include "arrow/flight/sql/example/sqlite_server.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+std::shared_ptr<DataType> GetDataTypeFromSqliteType(const int column_type) {
+  switch (column_type) {
+    case SQLITE_INTEGER:
+      return int64();
+    case SQLITE_FLOAT:
+      return float64();
+    case SQLITE_BLOB:
+      return binary();
+    case SQLITE_TEXT:
+      return utf8();
+    case SQLITE_NULL:
+    default:
+      return null();
+  }
+}
+
+arrow::Result<std::shared_ptr<SqliteStatement>> SqliteStatement::Create(
+    sqlite3* db, const std::string& sql) {
+  sqlite3_stmt* stmt = nullptr;
+  int rc =
+      sqlite3_prepare_v2(db, sql.c_str(), static_cast<int>(sql.size()), &stmt, NULLPTR);
+
+  if (rc != SQLITE_OK) {
+    std::string err_msg = "Can't prepare statement: " + std::string(sqlite3_errmsg(db));
+    if (stmt != nullptr) {
+      rc = sqlite3_finalize(stmt);
+      if (rc != SQLITE_OK) {
+        err_msg += "; Failed to finalize SQLite statement: ";
+        err_msg += std::string(sqlite3_errmsg(db));
+      }
+    }
+    return Status::Invalid(err_msg);
+  }
+
+  std::shared_ptr<SqliteStatement> result(new SqliteStatement(db, stmt));
+  return result;
+}
+
+arrow::Result<std::shared_ptr<Schema>> SqliteStatement::GetSchema() const {
+  std::vector<std::shared_ptr<Field>> fields;
+  int column_count = sqlite3_column_count(stmt_);
+  for (int i = 0; i < column_count; i++) {
+    const char* column_name = sqlite3_column_name(stmt_, i);
+
+    // SQLite does not always provide column types, especially when the statement has not
+    // been executed yet. Because of this behaviour this method tries to get the column
+    // types in two attempts:
+    // 1. Use sqlite3_column_type(), which return SQLITE_NULL if the statement has not
+    //    been executed yet
+    // 2. Use sqlite3_column_decltype(), which returns correctly if given column is
+    //    declared in the table.
+    // Because of this limitation, it is not possible to know the column types for some
+    // prepared statements, in this case it returns a dense_union type covering any type
+    // SQLite supports.
+    const int column_type = sqlite3_column_type(stmt_, i);
+    std::shared_ptr<DataType> data_type = GetDataTypeFromSqliteType(column_type);
+    if (data_type->id() == Type::NA) {
+      // Try to retrieve column type from sqlite3_column_decltype
+      const char* column_decltype = sqlite3_column_decltype(stmt_, i);
+      if (column_decltype != NULLPTR) {
+        data_type = GetArrowType(column_decltype);
+      } else {
+        // If it can not determine the actual column type, return a dense_union type
+        // covering any type SQLite supports.
+        data_type = GetUnknownColumnDataType();
+      }
+    }
+
+    fields.push_back(arrow::field(column_name, data_type));
+  }
+
+  return arrow::schema(fields);
+}
+
+SqliteStatement::~SqliteStatement() { sqlite3_finalize(stmt_); }
+
+arrow::Result<int> SqliteStatement::Step() {
+  int rc = sqlite3_step(stmt_);
+  if (rc == SQLITE_ERROR) {
+    return Status::ExecutionError("A SQLite runtime error has occurred: ",
+                                  sqlite3_errmsg(db_));
+  }
+
+  return rc;
+}
+
+arrow::Result<int> SqliteStatement::Reset() {
+  int rc = sqlite3_reset(stmt_);
+  if (rc == SQLITE_ERROR) {
+    return Status::ExecutionError("A SQLite runtime error has occurred: ",
+                                  sqlite3_errmsg(db_));
+  }
+
+  return rc;
+}
+
+sqlite3_stmt* SqliteStatement::GetSqlite3Stmt() const { return stmt_; }
+
+arrow::Result<int64_t> SqliteStatement::ExecuteUpdate() {
+  ARROW_RETURN_NOT_OK(Step());
+  return sqlite3_changes(db_);
+}
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_statement.h b/cpp/src/arrow/flight/sql/example/sqlite_statement.h
new file mode 100644
index 0000000..a3f086a
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_statement.h
@@ -0,0 +1,73 @@
+// 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.
+
+#pragma once
+
+#include <sqlite3.h>
+
+#include <memory>
+#include <string>
+
+#include "arrow/type_fwd.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+class SqliteStatement {
+ public:
+  /// \brief Creates a SQLite3 statement.
+  /// \param[in] db        SQLite3 database instance.
+  /// \param[in] sql       SQL statement.
+  /// \return              A SqliteStatement object.
+  static arrow::Result<std::shared_ptr<SqliteStatement>> Create(sqlite3* db,
+                                                                const std::string& sql);
+
+  ~SqliteStatement();
+
+  /// \brief Creates an Arrow Schema based on the results of this statement.
+  /// \return              The resulting Schema.
+  arrow::Result<std::shared_ptr<Schema>> GetSchema() const;
+
+  /// \brief Steps on underlying sqlite3_stmt.
+  /// \return          The resulting return code from SQLite.
+  arrow::Result<int> Step();
+
+  /// \brief Reset the state of the sqlite3_stmt.
+  /// \return          The resulting return code from SQLite.
+  arrow::Result<int> Reset();
+
+  /// \brief Returns the underlying sqlite3_stmt.
+  /// \return A sqlite statement.
+  sqlite3_stmt* GetSqlite3Stmt() const;
+
+  /// \brief Executes an UPDATE, INSERT or DELETE statement.
+  /// \return              The number of rows changed by execution.
+  arrow::Result<int64_t> ExecuteUpdate();
+
+ private:
+  sqlite3* db_;
+  sqlite3_stmt* stmt_;
+
+  SqliteStatement(sqlite3* db, sqlite3_stmt* stmt) : db_(db), stmt_(stmt) {}
+};
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.cc b/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.cc
new file mode 100644
index 0000000..a5824ae
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.cc
@@ -0,0 +1,189 @@
+// 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.
+
+#include "arrow/flight/sql/example/sqlite_statement_batch_reader.h"
+
+#include <sqlite3.h>
+
+#include "arrow/builder.h"
+#include "arrow/flight/sql/example/sqlite_statement.h"
+
+#define STRING_BUILDER_CASE(TYPE_CLASS, STMT, COLUMN)                             \
+  case TYPE_CLASS##Type::type_id: {                                               \
+    int bytes = sqlite3_column_bytes(STMT, COLUMN);                               \
+    const unsigned char* string = sqlite3_column_text(STMT, COLUMN);              \
+    if (string == nullptr) {                                                      \
+      ARROW_RETURN_NOT_OK(                                                        \
+          (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).AppendNull());        \
+      break;                                                                      \
+    }                                                                             \
+    ARROW_RETURN_NOT_OK(                                                          \
+        (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).Append(string, bytes)); \
+    break;                                                                        \
+  }
+
+#define BINARY_BUILDER_CASE(TYPE_CLASS, STMT, COLUMN)                                  \
+  case TYPE_CLASS##Type::type_id: {                                                    \
+    int bytes = sqlite3_column_bytes(STMT, COLUMN);                                    \
+    const void* blob = sqlite3_column_blob(STMT, COLUMN);                              \
+    if (blob == nullptr) {                                                             \
+      ARROW_RETURN_NOT_OK(                                                             \
+          (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).AppendNull());             \
+      break;                                                                           \
+    }                                                                                  \
+    ARROW_RETURN_NOT_OK(                                                               \
+        (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).Append((char*)blob, bytes)); \
+    break;                                                                             \
+  }
+
+#define INT_BUILDER_CASE(TYPE_CLASS, STMT, COLUMN)                         \
+  case TYPE_CLASS##Type::type_id: {                                        \
+    if (sqlite3_column_type(stmt_, i) == SQLITE_NULL) {                    \
+      ARROW_RETURN_NOT_OK(                                                 \
+          (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).AppendNull()); \
+      break;                                                               \
+    }                                                                      \
+    sqlite3_int64 value = sqlite3_column_int64(STMT, COLUMN);              \
+    ARROW_RETURN_NOT_OK(                                                   \
+        (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).Append(value));  \
+    break;                                                                 \
+  }
+
+#define FLOAT_BUILDER_CASE(TYPE_CLASS, STMT, COLUMN)                       \
+  case TYPE_CLASS##Type::type_id: {                                        \
+    if (sqlite3_column_type(stmt_, i) == SQLITE_NULL) {                    \
+      ARROW_RETURN_NOT_OK(                                                 \
+          (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).AppendNull()); \
+      break;                                                               \
+    }                                                                      \
+    double value = sqlite3_column_double(STMT, COLUMN);                    \
+    ARROW_RETURN_NOT_OK(                                                   \
+        (reinterpret_cast<TYPE_CLASS##Builder&>(builder)).Append(value));  \
+    break;                                                                 \
+  }
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+// Batch size for SQLite statement results
+static constexpr int kMaxBatchSize = 1024;
+
+std::shared_ptr<Schema> SqliteStatementBatchReader::schema() const { return schema_; }
+
+SqliteStatementBatchReader::SqliteStatementBatchReader(
+    std::shared_ptr<SqliteStatement> statement, std::shared_ptr<Schema> schema)
+    : statement_(std::move(statement)),
+      schema_(std::move(schema)),
+      rc_(SQLITE_OK),
+      already_executed_(false) {}
+
+Result<std::shared_ptr<SqliteStatementBatchReader>> SqliteStatementBatchReader::Create(
+    const std::shared_ptr<SqliteStatement>& statement_) {
+  ARROW_RETURN_NOT_OK(statement_->Step());
+
+  ARROW_ASSIGN_OR_RAISE(auto schema, statement_->GetSchema());
+
+  std::shared_ptr<SqliteStatementBatchReader> result(
+      new SqliteStatementBatchReader(statement_, schema));
+
+  return result;
+}
+
+arrow::Result<std::shared_ptr<SqliteStatementBatchReader>>
+SqliteStatementBatchReader::Create(const std::shared_ptr<SqliteStatement>& statement,
+                                   const std::shared_ptr<Schema>& schema) {
+  std::shared_ptr<SqliteStatementBatchReader> result(
+      new SqliteStatementBatchReader(statement, schema));
+
+  return result;
+}
+
+Status SqliteStatementBatchReader::ReadNext(std::shared_ptr<RecordBatch>* out) {
+  sqlite3_stmt* stmt_ = statement_->GetSqlite3Stmt();
+
+  const int num_fields = schema_->num_fields();
+  std::vector<std::unique_ptr<arrow::ArrayBuilder>> builders(num_fields);
+
+  for (int i = 0; i < num_fields; i++) {
+    const std::shared_ptr<Field>& field = schema_->field(i);
+    const std::shared_ptr<DataType>& field_type = field->type();
+
+    ARROW_RETURN_NOT_OK(MakeBuilder(default_memory_pool(), field_type, &builders[i]));
+  }
+
+  if (!already_executed_) {
+    ARROW_ASSIGN_OR_RAISE(rc_, statement_->Reset());
+    ARROW_ASSIGN_OR_RAISE(rc_, statement_->Step());
+    already_executed_ = true;
+  }
+
+  int64_t rows = 0;
+  while (rows < kMaxBatchSize && rc_ == SQLITE_ROW) {
+    rows++;
+    for (int i = 0; i < num_fields; i++) {
+      const std::shared_ptr<Field>& field = schema_->field(i);
+      const std::shared_ptr<DataType>& field_type = field->type();
+      ArrayBuilder& builder = *builders[i];
+
+      // NOTE: This is not the optimal way of building Arrow vectors.
+      // That would be to presize the builders to avoiding several resizing operations
+      // when appending values and also to build one vector at a time.
+      switch (field_type->id()) {
+        INT_BUILDER_CASE(Int64, stmt_, i)
+        INT_BUILDER_CASE(UInt64, stmt_, i)
+        INT_BUILDER_CASE(Int32, stmt_, i)
+        INT_BUILDER_CASE(UInt32, stmt_, i)
+        INT_BUILDER_CASE(Int16, stmt_, i)
+        INT_BUILDER_CASE(UInt16, stmt_, i)
+        INT_BUILDER_CASE(Int8, stmt_, i)
+        INT_BUILDER_CASE(UInt8, stmt_, i)
+        FLOAT_BUILDER_CASE(Double, stmt_, i)
+        FLOAT_BUILDER_CASE(Float, stmt_, i)
+        FLOAT_BUILDER_CASE(HalfFloat, stmt_, i)
+        BINARY_BUILDER_CASE(Binary, stmt_, i)
+        BINARY_BUILDER_CASE(LargeBinary, stmt_, i)
+        STRING_BUILDER_CASE(String, stmt_, i)
+        STRING_BUILDER_CASE(LargeString, stmt_, i)
+        default:
+          return Status::NotImplemented("Not implemented SQLite data conversion to ",
+                                        field_type->name());
+      }
+    }
+
+    ARROW_ASSIGN_OR_RAISE(rc_, statement_->Step());
+  }
+
+  if (rows > 0) {
+    std::vector<std::shared_ptr<Array>> arrays(builders.size());
+    for (int i = 0; i < num_fields; i++) {
+      ARROW_RETURN_NOT_OK(builders[i]->Finish(&arrays[i]));
+    }
+
+    *out = RecordBatch::Make(schema_, rows, arrays);
+  } else {
+    *out = NULLPTR;
+  }
+
+  return Status::OK();
+}
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.h b/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.h
new file mode 100644
index 0000000..8a6bc60
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_statement_batch_reader.h
@@ -0,0 +1,65 @@
+// 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.
+
+#pragma once
+
+#include <sqlite3.h>
+
+#include <memory>
+
+#include "arrow/flight/sql/example/sqlite_statement.h"
+#include "arrow/record_batch.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+class SqliteStatementBatchReader : public RecordBatchReader {
+ public:
+  /// \brief Creates a RecordBatchReader backed by a SQLite statement.
+  /// \param[in] statement    SQLite statement to be read.
+  /// \return                 A SqliteStatementBatchReader.
+  static arrow::Result<std::shared_ptr<SqliteStatementBatchReader>> Create(
+      const std::shared_ptr<SqliteStatement>& statement);
+
+  /// \brief Creates a RecordBatchReader backed by a SQLite statement.
+  /// \param[in] statement    SQLite statement to be read.
+  /// \param[in] schema       Schema to be used on results.
+  /// \return                 A SqliteStatementBatchReader..
+  static arrow::Result<std::shared_ptr<SqliteStatementBatchReader>> Create(
+      const std::shared_ptr<SqliteStatement>& statement,
+      const std::shared_ptr<Schema>& schema);
+
+  std::shared_ptr<Schema> schema() const override;
+
+  Status ReadNext(std::shared_ptr<RecordBatch>* out) override;
+
+ private:
+  std::shared_ptr<SqliteStatement> statement_;
+  std::shared_ptr<Schema> schema_;
+  int rc_;
+  bool already_executed_;
+
+  SqliteStatementBatchReader(std::shared_ptr<SqliteStatement> statement,
+                             std::shared_ptr<Schema> schema);
+};
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.cc b/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.cc
new file mode 100644
index 0000000..7fb68a7
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.cc
@@ -0,0 +1,106 @@
+// 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.
+
+#include "arrow/flight/sql/example/sqlite_tables_schema_batch_reader.h"
+
+#include <sqlite3.h>
+
+#include <sstream>
+
+#include "arrow/flight/sql/example/sqlite_server.h"
+#include "arrow/flight/sql/example/sqlite_statement.h"
+#include "arrow/flight/sql/server.h"
+#include "arrow/ipc/writer.h"
+#include "arrow/record_batch.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+std::shared_ptr<Schema> SqliteTablesWithSchemaBatchReader::schema() const {
+  return SqlSchema::GetTablesSchemaWithIncludedSchema();
+}
+
+Status SqliteTablesWithSchemaBatchReader::ReadNext(std::shared_ptr<RecordBatch>* batch) {
+  std::stringstream schema_query;
+
+  schema_query
+      << "SELECT table_name, name, type, [notnull] FROM pragma_table_info(table_name)"
+      << "JOIN(" << main_query_ << ") order by table_name";
+
+  std::shared_ptr<example::SqliteStatement> schema_statement;
+  ARROW_ASSIGN_OR_RAISE(schema_statement,
+                        example::SqliteStatement::Create(db_, schema_query.str()))
+
+  std::shared_ptr<RecordBatch> first_batch;
+
+  ARROW_RETURN_NOT_OK(reader_->ReadNext(&first_batch));
+
+  if (!first_batch) {
+    *batch = NULLPTR;
+    return Status::OK();
+  }
+
+  const std::shared_ptr<Array> table_name_array =
+      first_batch->GetColumnByName("table_name");
+
+  BinaryBuilder schema_builder;
+
+  auto* string_array = reinterpret_cast<StringArray*>(table_name_array.get());
+
+  std::vector<std::shared_ptr<Field>> column_fields;
+  for (int i = 0; i < table_name_array->length(); i++) {
+    const std::string& table_name = string_array->GetString(i);
+
+    while (sqlite3_step(schema_statement->GetSqlite3Stmt()) == SQLITE_ROW) {
+      std::string sqlite_table_name = std::string(reinterpret_cast<const char*>(
+          sqlite3_column_text(schema_statement->GetSqlite3Stmt(), 0)));
+      if (sqlite_table_name == table_name) {
+        const char* column_name = reinterpret_cast<const char*>(
+            sqlite3_column_text(schema_statement->GetSqlite3Stmt(), 1));
+        const char* column_type = reinterpret_cast<const char*>(
+            sqlite3_column_text(schema_statement->GetSqlite3Stmt(), 2));
+        int nullable = sqlite3_column_int(schema_statement->GetSqlite3Stmt(), 3);
+
+        column_fields.push_back(
+            arrow::field(column_name, GetArrowType(column_type), nullable == 0, NULL));
+      }
+    }
+    const arrow::Result<std::shared_ptr<Buffer>>& value =
+        ipc::SerializeSchema(*arrow::schema(column_fields));
+
+    std::shared_ptr<Buffer> schema_buffer;
+    ARROW_ASSIGN_OR_RAISE(schema_buffer, value);
+
+    column_fields.clear();
+    ARROW_RETURN_NOT_OK(
+        schema_builder.Append(schema_buffer->data(), schema_buffer->size()));
+  }
+
+  std::shared_ptr<Array> schema_array;
+  ARROW_RETURN_NOT_OK(schema_builder.Finish(&schema_array));
+
+  ARROW_ASSIGN_OR_RAISE(*batch, first_batch->AddColumn(4, "table_schema", schema_array));
+
+  return Status::OK();
+}
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.h b/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.h
new file mode 100644
index 0000000..ecba88e
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/example/sqlite_tables_schema_batch_reader.h
@@ -0,0 +1,58 @@
+// 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.
+
+#pragma once
+
+#include <sqlite3.h>
+
+#include <memory>
+#include <string>
+
+#include "arrow/flight/sql/example/sqlite_statement.h"
+#include "arrow/flight/sql/example/sqlite_statement_batch_reader.h"
+#include "arrow/record_batch.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace example {
+
+class SqliteTablesWithSchemaBatchReader : public RecordBatchReader {
+ private:
+  std::shared_ptr<example::SqliteStatementBatchReader> reader_;
+  std::string main_query_;
+  sqlite3* db_;
+
+ public:
+  /// Constructor for SqliteTablesWithSchemaBatchReader class
+  /// \param reader an shared_ptr from a SqliteStatementBatchReader.
+  /// \param main_query  SQL query that originated reader's data.
+  /// \param db     a pointer to the sqlite3 db.
+  SqliteTablesWithSchemaBatchReader(
+      std::shared_ptr<example::SqliteStatementBatchReader> reader, std::string main_query,
+      sqlite3* db)
+      : reader_(std::move(reader)), main_query_(std::move(main_query)), db_(db) {}
+
+  std::shared_ptr<Schema> schema() const override;
+
+  Status ReadNext(std::shared_ptr<RecordBatch>* batch) override;
+};
+
+}  // namespace example
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/server.cc b/cpp/src/arrow/flight/sql/server.cc
new file mode 100644
index 0000000..6d328c0
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/server.cc
@@ -0,0 +1,761 @@
+// 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.
+
+// Interfaces to use for defining Flight RPC servers. API should be considered
+// experimental for now
+
+#include "arrow/flight/sql/server.h"
+
+#include <google/protobuf/any.pb.h>
+
+#include "arrow/buffer.h"
+#include "arrow/builder.h"
+#include "arrow/flight/sql/FlightSql.pb.h"
+#include "arrow/flight/sql/sql_info_internal.h"
+#include "arrow/type.h"
+#include "arrow/util/checked_cast.h"
+
+#define PROPERTY_TO_OPTIONAL(COMMAND, PROPERTY) \
+  COMMAND.has_##PROPERTY() ? util::make_optional(COMMAND.PROPERTY()) : util::nullopt
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+namespace pb = arrow::flight::protocol;
+
+using arrow::internal::checked_cast;
+using arrow::internal::checked_pointer_cast;
+
+namespace {
+
+arrow::Result<GetCrossReference> ParseCommandGetCrossReference(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandGetCrossReference command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetCrossReference.");
+  }
+
+  GetCrossReference result;
+  result.pk_table_ref = {PROPERTY_TO_OPTIONAL(command, pk_catalog),
+                         PROPERTY_TO_OPTIONAL(command, pk_db_schema), command.pk_table()};
+  result.fk_table_ref = {PROPERTY_TO_OPTIONAL(command, fk_catalog),
+                         PROPERTY_TO_OPTIONAL(command, fk_db_schema), command.fk_table()};
+  return result;
+}
+
+arrow::Result<GetImportedKeys> ParseCommandGetImportedKeys(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandGetImportedKeys command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetImportedKeys.");
+  }
+
+  GetImportedKeys result;
+  result.table_ref = {PROPERTY_TO_OPTIONAL(command, catalog),
+                      PROPERTY_TO_OPTIONAL(command, db_schema), command.table()};
+  return result;
+}
+
+arrow::Result<GetExportedKeys> ParseCommandGetExportedKeys(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandGetExportedKeys command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetExportedKeys.");
+  }
+
+  GetExportedKeys result;
+  result.table_ref = {PROPERTY_TO_OPTIONAL(command, catalog),
+                      PROPERTY_TO_OPTIONAL(command, db_schema), command.table()};
+  return result;
+}
+
+arrow::Result<GetPrimaryKeys> ParseCommandGetPrimaryKeys(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandGetPrimaryKeys command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetPrimaryKeys.");
+  }
+
+  GetPrimaryKeys result;
+  result.table_ref = {PROPERTY_TO_OPTIONAL(command, catalog),
+                      PROPERTY_TO_OPTIONAL(command, db_schema), command.table()};
+  return result;
+}
+
+arrow::Result<GetSqlInfo> ParseCommandGetSqlInfo(
+    const google::protobuf::Any& any, const SqlInfoResultMap& sql_info_id_to_result) {
+  pb::sql::CommandGetSqlInfo command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetSqlInfo.");
+  }
+
+  GetSqlInfo result;
+  if (command.info_size() > 0) {
+    result.info.reserve(command.info_size());
+    result.info.assign(command.info().begin(), command.info().end());
+  } else {
+    result.info.reserve(sql_info_id_to_result.size());
+    for (const auto& it : sql_info_id_to_result) {
+      result.info.push_back(it.first);
+    }
+  }
+  return result;
+}
+
+arrow::Result<GetDbSchemas> ParseCommandGetDbSchemas(const google::protobuf::Any& any) {
+  pb::sql::CommandGetDbSchemas command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetDbSchemas.");
+  }
+
+  GetDbSchemas result;
+  result.catalog = PROPERTY_TO_OPTIONAL(command, catalog);
+  result.db_schema_filter_pattern =
+      PROPERTY_TO_OPTIONAL(command, db_schema_filter_pattern);
+  return result;
+}
+
+arrow::Result<PreparedStatementQuery> ParseCommandPreparedStatementQuery(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandPreparedStatementQuery command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandPreparedStatementQuery.");
+  }
+
+  PreparedStatementQuery result;
+  result.prepared_statement_handle = command.prepared_statement_handle();
+  return result;
+}
+
+arrow::Result<StatementQuery> ParseCommandStatementQuery(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandStatementQuery command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandStatementQuery.");
+  }
+
+  StatementQuery result;
+  result.query = command.query();
+  return result;
+}
+
+arrow::Result<GetTables> ParseCommandGetTables(const google::protobuf::Any& any) {
+  pb::sql::CommandGetTables command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandGetTables.");
+  }
+
+  std::vector<std::string> table_types(command.table_types_size());
+  std::copy(command.table_types().begin(), command.table_types().end(),
+            table_types.begin());
+
+  GetTables result;
+  result.catalog = PROPERTY_TO_OPTIONAL(command, catalog);
+  result.db_schema_filter_pattern =
+      PROPERTY_TO_OPTIONAL(command, db_schema_filter_pattern);
+  result.table_name_filter_pattern =
+      PROPERTY_TO_OPTIONAL(command, table_name_filter_pattern);
+  result.table_types = table_types;
+  result.include_schema = command.include_schema();
+  return result;
+}
+
+arrow::Result<StatementQueryTicket> ParseStatementQueryTicket(
+    const google::protobuf::Any& any) {
+  pb::sql::TicketStatementQuery command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack TicketStatementQuery.");
+  }
+
+  StatementQueryTicket result;
+  result.statement_handle = command.statement_handle();
+  return result;
+}
+
+arrow::Result<StatementUpdate> ParseCommandStatementUpdate(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandStatementUpdate command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandStatementUpdate.");
+  }
+
+  StatementUpdate result;
+  result.query = command.query();
+  return result;
+}
+
+arrow::Result<PreparedStatementUpdate> ParseCommandPreparedStatementUpdate(
+    const google::protobuf::Any& any) {
+  pb::sql::CommandPreparedStatementUpdate command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack CommandPreparedStatementUpdate.");
+  }
+
+  PreparedStatementUpdate result;
+  result.prepared_statement_handle = command.prepared_statement_handle();
+  return result;
+}
+
+arrow::Result<ActionCreatePreparedStatementRequest>
+ParseActionCreatePreparedStatementRequest(const google::protobuf::Any& any) {
+  pb::sql::ActionCreatePreparedStatementRequest command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack ActionCreatePreparedStatementRequest.");
+  }
+
+  ActionCreatePreparedStatementRequest result;
+  result.query = command.query();
+  return result;
+}
+
+arrow::Result<ActionClosePreparedStatementRequest>
+ParseActionClosePreparedStatementRequest(const google::protobuf::Any& any) {
+  pb::sql::ActionClosePreparedStatementRequest command;
+  if (!any.UnpackTo(&command)) {
+    return Status::Invalid("Unable to unpack ActionClosePreparedStatementRequest.");
+  }
+
+  ActionClosePreparedStatementRequest result;
+  result.prepared_statement_handle = command.prepared_statement_handle();
+  return result;
+}
+
+}  // namespace
+
+arrow::Result<std::string> CreateStatementQueryTicket(
+    const std::string& statement_handle) {
+  protocol::sql::TicketStatementQuery ticket_statement_query;
+  ticket_statement_query.set_statement_handle(statement_handle);
+
+  google::protobuf::Any ticket;
+  ticket.PackFrom(ticket_statement_query);
+
+  std::string ticket_string;
+
+  if (!ticket.SerializeToString(&ticket_string)) {
+    return Status::IOError("Invalid ticket.");
+  }
+  return ticket_string;
+}
+
+Status FlightSqlServerBase::GetFlightInfo(const ServerCallContext& context,
+                                          const FlightDescriptor& request,
+                                          std::unique_ptr<FlightInfo>* info) {
+  google::protobuf::Any any;
+  if (!any.ParseFromArray(request.cmd.data(), static_cast<int>(request.cmd.size()))) {
+    return Status::Invalid("Unable to parse command");
+  }
+
+  if (any.Is<pb::sql::CommandStatementQuery>()) {
+    ARROW_ASSIGN_OR_RAISE(StatementQuery internal_command,
+                          ParseCommandStatementQuery(any));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoStatement(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandPreparedStatementQuery>()) {
+    ARROW_ASSIGN_OR_RAISE(PreparedStatementQuery internal_command,
+                          ParseCommandPreparedStatementQuery(any));
+    ARROW_ASSIGN_OR_RAISE(
+        *info, GetFlightInfoPreparedStatement(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetCatalogs>()) {
+    ARROW_ASSIGN_OR_RAISE(*info, GetFlightInfoCatalogs(context, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetDbSchemas>()) {
+    ARROW_ASSIGN_OR_RAISE(GetDbSchemas internal_command, ParseCommandGetDbSchemas(any));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoSchemas(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetTables>()) {
+    ARROW_ASSIGN_OR_RAISE(GetTables command, ParseCommandGetTables(any));
+    ARROW_ASSIGN_OR_RAISE(*info, GetFlightInfoTables(context, command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetTableTypes>()) {
+    ARROW_ASSIGN_OR_RAISE(*info, GetFlightInfoTableTypes(context, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetSqlInfo>()) {
+    ARROW_ASSIGN_OR_RAISE(GetSqlInfo internal_command,
+                          ParseCommandGetSqlInfo(any, sql_info_id_to_result_));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoSqlInfo(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetPrimaryKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetPrimaryKeys internal_command,
+                          ParseCommandGetPrimaryKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoPrimaryKeys(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetExportedKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetExportedKeys internal_command,
+                          ParseCommandGetExportedKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoExportedKeys(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetImportedKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetImportedKeys internal_command,
+                          ParseCommandGetImportedKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*info,
+                          GetFlightInfoImportedKeys(context, internal_command, request));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetCrossReference>()) {
+    ARROW_ASSIGN_OR_RAISE(GetCrossReference internal_command,
+                          ParseCommandGetCrossReference(any));
+    ARROW_ASSIGN_OR_RAISE(
+        *info, GetFlightInfoCrossReference(context, internal_command, request));
+    return Status::OK();
+  }
+
+  return Status::Invalid("The defined request is invalid.");
+}
+
+Status FlightSqlServerBase::DoGet(const ServerCallContext& context, const Ticket& request,
+                                  std::unique_ptr<FlightDataStream>* stream) {
+  google::protobuf::Any any;
+
+  if (!any.ParseFromArray(request.ticket.data(),
+                          static_cast<int>(request.ticket.size()))) {
+    return Status::Invalid("Unable to parse ticket.");
+  }
+
+  if (any.Is<pb::sql::TicketStatementQuery>()) {
+    ARROW_ASSIGN_OR_RAISE(StatementQueryTicket command, ParseStatementQueryTicket(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetStatement(context, command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandPreparedStatementQuery>()) {
+    ARROW_ASSIGN_OR_RAISE(PreparedStatementQuery internal_command,
+                          ParseCommandPreparedStatementQuery(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetPreparedStatement(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetCatalogs>()) {
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetCatalogs(context));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetDbSchemas>()) {
+    ARROW_ASSIGN_OR_RAISE(GetDbSchemas internal_command, ParseCommandGetDbSchemas(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetDbSchemas(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetTables>()) {
+    ARROW_ASSIGN_OR_RAISE(GetTables command, ParseCommandGetTables(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetTables(context, command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetTableTypes>()) {
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetTableTypes(context));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetSqlInfo>()) {
+    ARROW_ASSIGN_OR_RAISE(GetSqlInfo internal_command,
+                          ParseCommandGetSqlInfo(any, sql_info_id_to_result_));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetSqlInfo(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetPrimaryKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetPrimaryKeys internal_command,
+                          ParseCommandGetPrimaryKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetPrimaryKeys(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetExportedKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetExportedKeys internal_command,
+                          ParseCommandGetExportedKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetExportedKeys(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetImportedKeys>()) {
+    ARROW_ASSIGN_OR_RAISE(GetImportedKeys internal_command,
+                          ParseCommandGetImportedKeys(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetImportedKeys(context, internal_command));
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandGetCrossReference>()) {
+    ARROW_ASSIGN_OR_RAISE(GetCrossReference internal_command,
+                          ParseCommandGetCrossReference(any));
+    ARROW_ASSIGN_OR_RAISE(*stream, DoGetCrossReference(context, internal_command));
+    return Status::OK();
+  }
+
+  return Status::Invalid("The defined request is invalid.");
+}
+
+Status FlightSqlServerBase::DoPut(const ServerCallContext& context,
+                                  std::unique_ptr<FlightMessageReader> reader,
+                                  std::unique_ptr<FlightMetadataWriter> writer) {
+  const FlightDescriptor& request = reader->descriptor();
+
+  google::protobuf::Any any;
+  if (!any.ParseFromArray(request.cmd.data(), static_cast<int>(request.cmd.size()))) {
+    return Status::Invalid("Unable to parse command.");
+  }
+
+  if (any.Is<pb::sql::CommandStatementUpdate>()) {
+    ARROW_ASSIGN_OR_RAISE(StatementUpdate internal_command,
+                          ParseCommandStatementUpdate(any));
+    ARROW_ASSIGN_OR_RAISE(auto record_count,
+                          DoPutCommandStatementUpdate(context, internal_command))
+
+    pb::sql::DoPutUpdateResult result;
+    result.set_record_count(record_count);
+
+    const auto buffer = Buffer::FromString(result.SerializeAsString());
+    ARROW_RETURN_NOT_OK(writer->WriteMetadata(*buffer));
+
+    return Status::OK();
+  } else if (any.Is<pb::sql::CommandPreparedStatementQuery>()) {
+    ARROW_ASSIGN_OR_RAISE(PreparedStatementQuery internal_command,
+                          ParseCommandPreparedStatementQuery(any));
+    return DoPutPreparedStatementQuery(context, internal_command, reader.get(),
+                                       writer.get());
+  } else if (any.Is<pb::sql::CommandPreparedStatementUpdate>()) {
+    ARROW_ASSIGN_OR_RAISE(PreparedStatementUpdate internal_command,
+                          ParseCommandPreparedStatementUpdate(any));
+    ARROW_ASSIGN_OR_RAISE(auto record_count, DoPutPreparedStatementUpdate(
+                                                 context, internal_command, reader.get()))
+
+    pb::sql::DoPutUpdateResult result;
+    result.set_record_count(record_count);
+
+    const auto buffer = Buffer::FromString(result.SerializeAsString());
+    ARROW_RETURN_NOT_OK(writer->WriteMetadata(*buffer));
+
+    return Status::OK();
+  }
+
+  return Status::Invalid("The defined request is invalid.");
+}
+
+Status FlightSqlServerBase::ListActions(const ServerCallContext& context,
+                                        std::vector<ActionType>* actions) {
+  *actions = {FlightSqlServerBase::kCreatePreparedStatementActionType,
+              FlightSqlServerBase::kClosePreparedStatementActionType};
+  return Status::OK();
+}
+
+Status FlightSqlServerBase::DoAction(const ServerCallContext& context,
+                                     const Action& action,
+                                     std::unique_ptr<ResultStream>* result_stream) {
+  if (action.type == FlightSqlServerBase::kCreatePreparedStatementActionType.type) {
+    google::protobuf::Any any_command;
+    if (!any_command.ParseFromArray(action.body->data(),
+                                    static_cast<int>(action.body->size()))) {
+      return Status::Invalid("Unable to parse action.");
+    }
+
+    ARROW_ASSIGN_OR_RAISE(ActionCreatePreparedStatementRequest internal_command,
+                          ParseActionCreatePreparedStatementRequest(any_command));
+    ARROW_ASSIGN_OR_RAISE(auto result, CreatePreparedStatement(context, internal_command))
+
+    pb::sql::ActionCreatePreparedStatementResult action_result;
+    action_result.set_prepared_statement_handle(result.prepared_statement_handle);
+    if (result.dataset_schema != nullptr) {
+      ARROW_ASSIGN_OR_RAISE(auto serialized_dataset_schema,
+                            ipc::SerializeSchema(*result.dataset_schema))
+      action_result.set_dataset_schema(serialized_dataset_schema->ToString());
+    }
+    if (result.parameter_schema != nullptr) {
+      ARROW_ASSIGN_OR_RAISE(auto serialized_parameter_schema,
+                            ipc::SerializeSchema(*result.parameter_schema))
+      action_result.set_parameter_schema(serialized_parameter_schema->ToString());
+    }
+
+    google::protobuf::Any any;
+    any.PackFrom(action_result);
+
+    auto buf = Buffer::FromString(any.SerializeAsString());
+    *result_stream = std::unique_ptr<ResultStream>(new SimpleResultStream({Result{buf}}));
+
+    return Status::OK();
+  } else if (action.type == FlightSqlServerBase::kClosePreparedStatementActionType.type) {
+    google::protobuf::Any any;
+    if (!any.ParseFromArray(action.body->data(), static_cast<int>(action.body->size()))) {
+      return Status::Invalid("Unable to parse action.");
+    }
+
+    ARROW_ASSIGN_OR_RAISE(ActionClosePreparedStatementRequest internal_command,
+                          ParseActionClosePreparedStatementRequest(any));
+
+    ARROW_RETURN_NOT_OK(ClosePreparedStatement(context, internal_command));
+
+    // Need to instantiate a ResultStream, otherwise clients can not wait for completion.
+    *result_stream = std::unique_ptr<ResultStream>(new SimpleResultStream({}));
+    return Status::OK();
+  }
+  return Status::Invalid("The defined request is invalid.");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoCatalogs(
+    const ServerCallContext& context, const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoCatalogs not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetCatalogs(
+    const ServerCallContext& context) {
+  return Status::NotImplemented("DoGetCatalogs not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoStatement(
+    const ServerCallContext& context, const StatementQuery& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoStatement not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetStatement(
+    const ServerCallContext& context, const StatementQueryTicket& command) {
+  return Status::NotImplemented("DoGetStatement not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+FlightSqlServerBase::GetFlightInfoPreparedStatement(const ServerCallContext& context,
+                                                    const PreparedStatementQuery& command,
+                                                    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoPreparedStatement not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>>
+FlightSqlServerBase::DoGetPreparedStatement(const ServerCallContext& context,
+                                            const PreparedStatementQuery& command) {
+  return Status::NotImplemented("DoGetPreparedStatement not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoSqlInfo(
+    const ServerCallContext& context, const GetSqlInfo& command,
+    const FlightDescriptor& descriptor) {
+  if (sql_info_id_to_result_.empty()) {
+    return Status::KeyError("No SQL information available.");
+  }
+
+  std::vector<FlightEndpoint> endpoints{FlightEndpoint{{descriptor.cmd}, {}}};
+  ARROW_ASSIGN_OR_RAISE(auto result, FlightInfo::Make(*SqlSchema::GetSqlInfoSchema(),
+                                                      descriptor, endpoints, -1, -1))
+
+  return std::unique_ptr<FlightInfo>(new FlightInfo(result));
+}
+
+void FlightSqlServerBase::RegisterSqlInfo(int32_t id, const SqlInfoResult& result) {
+  sql_info_id_to_result_[id] = result;
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetSqlInfo(
+    const ServerCallContext& context, const GetSqlInfo& command) {
+  MemoryPool* memory_pool = default_memory_pool();
+  UInt32Builder name_field_builder(memory_pool);
+  std::unique_ptr<ArrayBuilder> value_field_builder;
+  const auto& value_field_type = checked_pointer_cast<DenseUnionType>(
+      SqlSchema::GetSqlInfoSchema()->fields()[1]->type());
+  ARROW_RETURN_NOT_OK(MakeBuilder(memory_pool, value_field_type, &value_field_builder));
+
+  internal::SqlInfoResultAppender sql_info_result_appender(
+      checked_cast<DenseUnionBuilder*>(value_field_builder.get()));
+
+  // Populate both name_field_builder and value_field_builder for each element
+  // on command.info.
+  // value_field_builder is populated differently depending on the data type (as it is
+  // a DenseUnionBuilder). The population for each data type is implemented on
+  // internal::SqlInfoResultAppender.
+  for (const auto& info : command.info) {
+    const auto it = sql_info_id_to_result_.find(info);
+    if (it == sql_info_id_to_result_.end()) {
+      return Status::KeyError("No information for SQL info number ", info);
+    }
+    ARROW_RETURN_NOT_OK(name_field_builder.Append(info));
+    ARROW_RETURN_NOT_OK(arrow::util::visit(sql_info_result_appender, it->second));
+  }
+
+  std::shared_ptr<Array> name;
+  ARROW_RETURN_NOT_OK(name_field_builder.Finish(&name));
+  std::shared_ptr<Array> value;
+  ARROW_RETURN_NOT_OK(value_field_builder->Finish(&value));
+
+  auto row_count = static_cast<int64_t>(command.info.size());
+  const std::shared_ptr<RecordBatch>& batch =
+      RecordBatch::Make(SqlSchema::GetSqlInfoSchema(), row_count, {name, value});
+  ARROW_ASSIGN_OR_RAISE(const auto reader, RecordBatchReader::Make({batch}));
+
+  return std::unique_ptr<FlightDataStream>(new RecordBatchStream(reader));
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoSchemas(
+    const ServerCallContext& context, const GetDbSchemas& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoSchemas not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetDbSchemas(
+    const ServerCallContext& context, const GetDbSchemas& command) {
+  return Status::NotImplemented("DoGetDbSchemas not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoTables(
+    const ServerCallContext& context, const GetTables& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoTables not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetTables(
+    const ServerCallContext& context, const GetTables& command) {
+  return Status::NotImplemented("DoGetTables not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoTableTypes(
+    const ServerCallContext& context, const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoTableTypes not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetTableTypes(
+    const ServerCallContext& context) {
+  return Status::NotImplemented("DoGetTableTypes not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoPrimaryKeys(
+    const ServerCallContext& context, const GetPrimaryKeys& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoPrimaryKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetPrimaryKeys(
+    const ServerCallContext& context, const GetPrimaryKeys& command) {
+  return Status::NotImplemented("DoGetPrimaryKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoExportedKeys(
+    const ServerCallContext& context, const GetExportedKeys& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoExportedKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetExportedKeys(
+    const ServerCallContext& context, const GetExportedKeys& command) {
+  return Status::NotImplemented("DoGetExportedKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>> FlightSqlServerBase::GetFlightInfoImportedKeys(
+    const ServerCallContext& context, const GetImportedKeys& command,
+    const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoImportedKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetImportedKeys(
+    const ServerCallContext& context, const GetImportedKeys& command) {
+  return Status::NotImplemented("DoGetImportedKeys not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightInfo>>
+FlightSqlServerBase::GetFlightInfoCrossReference(const ServerCallContext& context,
+                                                 const GetCrossReference& command,
+                                                 const FlightDescriptor& descriptor) {
+  return Status::NotImplemented("GetFlightInfoCrossReference not implemented");
+}
+
+arrow::Result<std::unique_ptr<FlightDataStream>> FlightSqlServerBase::DoGetCrossReference(
+    const ServerCallContext& context, const GetCrossReference& command) {
+  return Status::NotImplemented("DoGetCrossReference not implemented");
+}
+
+arrow::Result<ActionCreatePreparedStatementResult>
+FlightSqlServerBase::CreatePreparedStatement(
+    const ServerCallContext& context,
+    const ActionCreatePreparedStatementRequest& request) {
+  return Status::NotImplemented("CreatePreparedStatement not implemented");
+}
+
+Status FlightSqlServerBase::ClosePreparedStatement(
+    const ServerCallContext& context,
+    const ActionClosePreparedStatementRequest& request) {
+  return Status::NotImplemented("ClosePreparedStatement not implemented");
+}
+
+Status FlightSqlServerBase::DoPutPreparedStatementQuery(
+    const ServerCallContext& context, const PreparedStatementQuery& command,
+    FlightMessageReader* reader, FlightMetadataWriter* writer) {
+  return Status::NotImplemented("DoPutPreparedStatementQuery not implemented");
+}
+
+arrow::Result<int64_t> FlightSqlServerBase::DoPutPreparedStatementUpdate(
+    const ServerCallContext& context, const PreparedStatementUpdate& command,
+    FlightMessageReader* reader) {
+  return Status::NotImplemented("DoPutPreparedStatementUpdate not implemented");
+}
+
+arrow::Result<int64_t> FlightSqlServerBase::DoPutCommandStatementUpdate(
+    const ServerCallContext& context, const StatementUpdate& command) {
+  return Status::NotImplemented("DoPutCommandStatementUpdate not implemented");
+}
+
+std::shared_ptr<Schema> SqlSchema::GetCatalogsSchema() {
+  return arrow::schema({field("catalog_name", utf8())});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetDbSchemasSchema() {
+  return arrow::schema(
+      {field("catalog_name", utf8()), field("db_schema_name", utf8(), false)});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetTablesSchema() {
+  return arrow::schema({field("catalog_name", utf8()), field("db_schema_name", utf8()),
+                        field("table_name", utf8()), field("table_type", utf8())});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetTablesSchemaWithIncludedSchema() {
+  return arrow::schema({field("catalog_name", utf8()), field("db_schema_name", utf8()),
+                        field("table_name", utf8()), field("table_type", utf8()),
+                        field("table_schema", binary())});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetTableTypesSchema() {
+  return arrow::schema({field("table_type", utf8())});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetPrimaryKeysSchema() {
+  return arrow::schema({field("catalog_name", utf8()), field("db_schema_name", utf8()),
+                        field("table_name", utf8()), field("column_name", utf8()),
+                        field("key_sequence", int64()), field("key_name", utf8())});
+}
+
+std::shared_ptr<Schema> GetImportedExportedKeysAndCrossReferenceSchema() {
+  return arrow::schema(
+      {field("pk_catalog_name", utf8(), true), field("pk_db_schema_name", utf8(), true),
+       field("pk_table_name", utf8(), false), field("pk_column_name", utf8(), false),
+       field("fk_catalog_name", utf8(), true), field("fk_db_schema_name", utf8(), true),
+       field("fk_table_name", utf8(), false), field("fk_column_name", utf8(), false),
+       field("key_sequence", int32(), false), field("fk_key_name", utf8(), true),
+       field("pk_key_name", utf8(), true), field("update_rule", uint8(), false),
+       field("delete_rule", uint8(), false)});
+}
+
+std::shared_ptr<Schema> SqlSchema::GetImportedKeysSchema() {
+  return GetImportedExportedKeysAndCrossReferenceSchema();
+}
+
+std::shared_ptr<Schema> SqlSchema::GetExportedKeysSchema() {
+  return GetImportedExportedKeysAndCrossReferenceSchema();
+}
+
+std::shared_ptr<Schema> SqlSchema::GetCrossReferenceSchema() {
+  return GetImportedExportedKeysAndCrossReferenceSchema();
+}
+
+std::shared_ptr<Schema> SqlSchema::GetSqlInfoSchema() {
+  return arrow::schema({field("name", uint32(), false),
+                        field("value",
+                              dense_union({field("string_value", utf8(), false),
+                                           field("bool_value", boolean(), false),
+                                           field("bigint_value", int64(), false),
+                                           field("int32_bitmask", int32(), false),
+                                           field("string_list", list(utf8()), false),
+                                           field("int32_to_int32_list_map",
+                                                 map(int32(), list(int32())), false)}),
+                              false)});
+}
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
+
+#undef PROPERTY_TO_OPTIONAL
diff --git a/cpp/src/arrow/flight/sql/server.h b/cpp/src/arrow/flight/sql/server.h
new file mode 100644
index 0000000..1d61016
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/server.h
@@ -0,0 +1,443 @@
+// 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.
+
+// Interfaces to use for defining Flight RPC servers. API should be considered
+// experimental for now
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "arrow/flight/server.h"
+#include "arrow/flight/sql/server.h"
+#include "arrow/flight/sql/types.h"
+#include "arrow/util/optional.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+struct StatementQuery {
+  std::string query;
+};
+
+struct StatementUpdate {
+  std::string query;
+};
+
+struct StatementQueryTicket {
+  std::string statement_handle;
+};
+
+struct PreparedStatementQuery {
+  std::string prepared_statement_handle;
+};
+
+struct PreparedStatementUpdate {
+  std::string prepared_statement_handle;
+};
+
+struct GetSqlInfo {
+  std::vector<int32_t> info;
+};
+
+struct GetDbSchemas {
+  util::optional<std::string> catalog;
+  util::optional<std::string> db_schema_filter_pattern;
+};
+
+struct GetTables {
+  util::optional<std::string> catalog;
+  util::optional<std::string> db_schema_filter_pattern;
+  util::optional<std::string> table_name_filter_pattern;
+  std::vector<std::string> table_types;
+  bool include_schema;
+};
+
+struct GetPrimaryKeys {
+  TableRef table_ref;
+};
+
+struct GetExportedKeys {
+  TableRef table_ref;
+};
+
+struct GetImportedKeys {
+  TableRef table_ref;
+};
+
+struct GetCrossReference {
+  TableRef pk_table_ref;
+  TableRef fk_table_ref;
+};
+
+struct ActionCreatePreparedStatementRequest {
+  std::string query;
+};
+
+struct ActionClosePreparedStatementRequest {
+  std::string prepared_statement_handle;
+};
+
+struct ActionCreatePreparedStatementResult {
+  std::shared_ptr<Schema> dataset_schema;
+  std::shared_ptr<Schema> parameter_schema;
+  std::string prepared_statement_handle;
+};
+
+/// \brief A utility function to create a ticket (a opaque binary token that the server
+///        uses to identify this query) for a statement query.
+///        Intended for Flight SQL server implementations.
+/// \param[in] statement_handle      The statement handle that will originate the ticket.
+/// \return                          The parsed ticket as an string.
+arrow::Result<std::string> CreateStatementQueryTicket(
+    const std::string& statement_handle);
+
+class ARROW_EXPORT FlightSqlServerBase : public FlightServerBase {
+ private:
+  SqlInfoResultMap sql_info_id_to_result_;
+
+ public:
+  Status GetFlightInfo(const ServerCallContext& context, const FlightDescriptor& request,
+                       std::unique_ptr<FlightInfo>* info) override;
+
+  Status DoGet(const ServerCallContext& context, const Ticket& request,
+               std::unique_ptr<FlightDataStream>* stream) override;
+
+  Status DoPut(const ServerCallContext& context,
+               std::unique_ptr<FlightMessageReader> reader,
+               std::unique_ptr<FlightMetadataWriter> writer) override;
+
+  const ActionType kCreatePreparedStatementActionType =
+      ActionType{"CreatePreparedStatement",
+                 "Creates a reusable prepared statement resource on the server.\n"
+                 "Request Message: ActionCreatePreparedStatementRequest\n"
+                 "Response Message: ActionCreatePreparedStatementResult"};
+  const ActionType kClosePreparedStatementActionType =
+      ActionType{"ClosePreparedStatement",
+                 "Closes a reusable prepared statement resource on the server.\n"
+                 "Request Message: ActionClosePreparedStatementRequest\n"
+                 "Response Message: N/A"};
+
+  Status ListActions(const ServerCallContext& context,
+                     std::vector<ActionType>* actions) override;
+
+  Status DoAction(const ServerCallContext& context, const Action& action,
+                  std::unique_ptr<ResultStream>* result) override;
+
+  /// \brief Get a FlightInfo for executing a SQL query.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The StatementQuery object containing the SQL statement.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoStatement(
+      const ServerCallContext& context, const StatementQuery& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the query results.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The StatementQueryTicket containing the statement handle.
+  /// \return                 The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetStatement(
+      const ServerCallContext& context, const StatementQueryTicket& command);
+
+  /// \brief Get a FlightInfo for executing an already created prepared statement.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The PreparedStatementQuery object containing the
+  ///                         prepared statement handle.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the prepared statement query results.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The PreparedStatementQuery object containing the
+  ///                         prepared statement handle.
+  /// \return                 The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPreparedStatement(
+      const ServerCallContext& context, const PreparedStatementQuery& command);
+
+  /// \brief Get a FlightInfo for listing catalogs.
+  /// \param[in] context      Per-call context.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCatalogs(
+      const ServerCallContext& context, const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the list of catalogs.
+  /// \param[in] context  Per-call context.
+  /// \return             An interface for sending data back to the client.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCatalogs(
+      const ServerCallContext& context);
+
+  /// \brief Get a FlightInfo for retrieving other information (See SqlInfo).
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetSqlInfo object containing the list of SqlInfo
+  ///                         to be returned.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoSqlInfo(
+      const ServerCallContext& context, const GetSqlInfo& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the list of SqlInfo results.
+  /// \param[in] context    Per-call context.
+  /// \param[in] command    The GetSqlInfo object containing the list of SqlInfo
+  ///                       to be returned.
+  /// \return               The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetSqlInfo(
+      const ServerCallContext& context, const GetSqlInfo& command);
+
+  /// \brief Get a FlightInfo for listing schemas.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetDbSchemas object which may contain filters for
+  ///                         catalog and schema name.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the list of schemas.
+  /// \param[in] context   Per-call context.
+  /// \param[in] command   The GetDbSchemas object which may contain filters for
+  ///                      catalog and schema name.
+  /// \return              The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetDbSchemas(
+      const ServerCallContext& context, const GetDbSchemas& command);
+
+  ///\brief Get a FlightInfo for listing tables.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetTables object which may contain filters for
+  ///                         catalog, schema and table names.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTables(
+      const ServerCallContext& context, const GetTables& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the list of tables.
+  /// \param[in] context   Per-call context.
+  /// \param[in] command   The GetTables object which may contain filters for
+  ///                      catalog, schema and table names.
+  /// \return              The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTables(
+      const ServerCallContext& context, const GetTables& command);
+
+  /// \brief Get a FlightInfo to extract information about the table types.
+  /// \param[in] context      Per-call context.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoTableTypes(
+      const ServerCallContext& context, const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the data related to the table types.
+  /// \param[in] context   Per-call context.
+  /// \return              The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetTableTypes(
+      const ServerCallContext& context);
+
+  /// \brief Get a FlightInfo to extract information about primary and foreign keys.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetPrimaryKeys object with necessary information
+  ///                         to execute the request.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the data related to the primary and
+  /// foreign
+  ///        keys.
+  /// \param[in] context  Per-call context.
+  /// \param[in] command  The GetPrimaryKeys object with necessary information
+  ///                     to execute the request.
+  /// \return             The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetPrimaryKeys(
+      const ServerCallContext& context, const GetPrimaryKeys& command);
+
+  /// \brief Get a FlightInfo to extract information about foreign and primary keys.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetExportedKeys object with necessary information
+  ///                         to execute the request.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the data related to the foreign and
+  /// primary
+  ///        keys.
+  /// \param[in] context  Per-call context.
+  /// \param[in] command  The GetExportedKeys object with necessary information
+  ///                     to execute the request.
+  /// \return             The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetExportedKeys(
+      const ServerCallContext& context, const GetExportedKeys& command);
+
+  /// \brief Get a FlightInfo to extract information about foreign and primary keys.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetImportedKeys object with necessary information
+  ///                         to execute the request.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the data related to the foreign and
+  ///        primary keys.
+  /// \param[in] context  Per-call context.
+  /// \param[in] command  The GetImportedKeys object with necessary information
+  ///                     to execute the request.
+  /// \return             The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetImportedKeys(
+      const ServerCallContext& context, const GetImportedKeys& command);
+
+  /// \brief Get a FlightInfo to extract information about foreign and primary keys.
+  /// \param[in] context      Per-call context.
+  /// \param[in] command      The GetCrossReference object with necessary
+  /// information
+  ///                         to execute the request.
+  /// \param[in] descriptor   The descriptor identifying the data stream.
+  /// \return                 The FlightInfo describing where to access the
+  ///                         dataset.
+  virtual arrow::Result<std::unique_ptr<FlightInfo>> GetFlightInfoCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command,
+      const FlightDescriptor& descriptor);
+
+  /// \brief Get a FlightDataStream containing the data related to the foreign and
+  ///        primary keys.
+  /// \param[in] context  Per-call context.
+  /// \param[in] command  The GetCrossReference object with necessary information
+  ///                     to execute the request.
+  /// \return             The FlightDataStream containing the results.
+  virtual arrow::Result<std::unique_ptr<FlightDataStream>> DoGetCrossReference(
+      const ServerCallContext& context, const GetCrossReference& command);
+
+  /// \brief Execute an update SQL statement.
+  /// \param[in] context  The call context.
+  /// \param[in] command  The StatementUpdate object containing the SQL statement.
+  /// \return             The changed record count.
+  virtual arrow::Result<int64_t> DoPutCommandStatementUpdate(
+      const ServerCallContext& context, const StatementUpdate& command);
+
+  /// \brief Create a prepared statement from given SQL statement.
+  /// \param[in] context  The call context.
+  /// \param[in] request  The ActionCreatePreparedStatementRequest object containing the
+  ///                     SQL statement.
+  /// \return             A ActionCreatePreparedStatementResult containing the dataset
+  ///                     and parameter schemas and a handle for created statement.
+  virtual arrow::Result<ActionCreatePreparedStatementResult> CreatePreparedStatement(
+      const ServerCallContext& context,
+      const ActionCreatePreparedStatementRequest& request);
+
+  /// \brief Close a prepared statement.
+  /// \param[in] context  The call context.
+  /// \param[in] request  The ActionClosePreparedStatementRequest object containing the
+  ///                     prepared statement handle.
+  virtual Status ClosePreparedStatement(
+      const ServerCallContext& context,
+      const ActionClosePreparedStatementRequest& request);
+
+  /// \brief Bind parameters to given prepared statement.
+  /// \param[in] context  The call context.
+  /// \param[in] command  The PreparedStatementQuery object containing the
+  ///                     prepared statement handle.
+  /// \param[in] reader   A sequence of uploaded record batches.
+  /// \param[in] writer   Send metadata back to the client.
+  virtual Status DoPutPreparedStatementQuery(const ServerCallContext& context,
+                                             const PreparedStatementQuery& command,
+                                             FlightMessageReader* reader,
+                                             FlightMetadataWriter* writer);
+
+  /// \brief Execute an update SQL prepared statement.
+  /// \param[in] context  The call context.
+  /// \param[in] command  The PreparedStatementUpdate object containing the
+  ///                     prepared statement handle.
+  /// \param[in] reader   a sequence of uploaded record batches.
+  /// \return             The changed record count.
+  virtual arrow::Result<int64_t> DoPutPreparedStatementUpdate(
+      const ServerCallContext& context, const PreparedStatementUpdate& command,
+      FlightMessageReader* reader);
+
+  /// \brief Register a new SqlInfo result, making it available when calling GetSqlInfo.
+  /// \param[in] id the SqlInfo identifier.
+  /// \param[in] result the result.
+  void RegisterSqlInfo(int32_t id, const SqlInfoResult& result);
+};
+
+/// \brief Auxiliary class containing all Schemas used on Flight SQL.
+class ARROW_EXPORT SqlSchema {
+ public:
+  /// \brief Get the Schema used on GetCatalogs response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetCatalogsSchema();
+
+  /// \brief Get the Schema used on GetDbSchemas response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetDbSchemasSchema();
+
+  /// \brief Get the Schema used on GetTables response when included schema
+  /// flags is set to false.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetTablesSchema();
+
+  /// \brief Get the Schema used on GetTables response when included schema
+  /// flags is set to true.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetTablesSchemaWithIncludedSchema();
+
+  /// \brief Get the Schema used on GetTableTypes response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetTableTypesSchema();
+
+  /// \brief Get the Schema used on GetPrimaryKeys response when included schema
+  /// flags is set to true.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetPrimaryKeysSchema();
+
+  /// \brief Get the Schema used on GetImportedKeys response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetExportedKeysSchema();
+
+  /// \brief Get the Schema used on GetImportedKeys response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetImportedKeysSchema();
+
+  /// \brief Get the Schema used on GetCrossReference response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetCrossReferenceSchema();
+
+  /// \brief Get the Schema used on GetSqlInfo response.
+  /// \return The default schema template.
+  static std::shared_ptr<Schema> GetSqlInfoSchema();
+};
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/server_test.cc b/cpp/src/arrow/flight/sql/server_test.cc
new file mode 100644
index 0000000..8dfea7a
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/server_test.cc
@@ -0,0 +1,767 @@
+// 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.
+
+#include "arrow/flight/sql/server.h"
+
+#include <arrow/util/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <condition_variable>
+#include <thread>
+
+#include "arrow/flight/api.h"
+#include "arrow/flight/sql/api.h"
+#include "arrow/flight/sql/example/sqlite_server.h"
+#include "arrow/flight/sql/example/sqlite_sql_info.h"
+#include "arrow/flight/test_util.h"
+#include "arrow/flight/types.h"
+#include "arrow/testing/gtest_util.h"
+
+using ::testing::_;
+using ::testing::Ref;
+
+using arrow::internal::checked_cast;
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+/// \brief Auxiliary variant visitor used to assert that GetSqlInfo's values are
+/// correctly placed on its DenseUnionArray
+class SqlInfoDenseUnionValidator {
+ private:
+  const DenseUnionScalar& data;
+
+ public:
+  /// \brief Asserts that the current DenseUnionScalar equals to given string value
+  void operator()(const std::string& string_value) const {
+    const auto& scalar = checked_cast<const StringScalar&>(*data.value);
+    ASSERT_EQ(string_value, scalar.ToString());
+  }
+
+  /// \brief Asserts that the current DenseUnionScalar equals to given bool value
+  void operator()(const bool bool_value) const {
+    const auto& scalar = checked_cast<const BooleanScalar&>(*data.value);
+    ASSERT_EQ(bool_value, scalar.value);
+  }
+
+  /// \brief Asserts that the current DenseUnionScalar equals to given int64_t value
+  void operator()(const int64_t bigint_value) const {
+    const auto& scalar = checked_cast<const Int64Scalar&>(*data.value);
+    ASSERT_EQ(bigint_value, scalar.value);
+  }
+
+  /// \brief Asserts that the current DenseUnionScalar equals to given int32_t value
+  void operator()(const int32_t int32_bitmask) const {
+    const auto& scalar = checked_cast<const Int32Scalar&>(*data.value);
+    ASSERT_EQ(int32_bitmask, scalar.value);
+  }
+
+  /// \brief Asserts that the current DenseUnionScalar equals to given string list
+  void operator()(const std::vector<std::string>& string_list) const {
+    const auto& array = checked_cast<const StringArray&>(
+        *(checked_cast<const ListScalar&>(*data.value).value));
+
+    ASSERT_EQ(string_list.size(), array.length());
+
+    for (size_t index = 0; index < string_list.size(); index++) {
+      ASSERT_EQ(string_list[index], array.GetString(index));
+    }
+  }
+
+  /// \brief Asserts that the current DenseUnionScalar equals to given int32 to int32 list
+  /// map.
+  void operator()(const std::unordered_map<int32_t, std::vector<int32_t>>&
+                      int32_to_int32_list) const {
+    const auto& struct_array = checked_cast<const StructArray&>(
+        *checked_cast<const MapScalar&>(*data.value).value);
+    const auto& keys = checked_cast<const Int32Array&>(*struct_array.field(0));
+    const auto& values = checked_cast<const ListArray&>(*struct_array.field(1));
+
+    // Assert that the given map has the right size
+    ASSERT_EQ(int32_to_int32_list.size(), keys.length());
+
+    // For each element on given MapScalar, assert it matches the argument
+    for (int i = 0; i < keys.length(); i++) {
+      ASSERT_OK_AND_ASSIGN(const auto& key_scalar, keys.GetScalar(i));
+      int32_t sql_info_id = checked_cast<const Int32Scalar&>(*key_scalar).value;
+
+      // Assert the key (SqlInfo id) exists
+      ASSERT_TRUE(int32_to_int32_list.count(sql_info_id));
+
+      const std::vector<int32_t>& expected_int32_list =
+          int32_to_int32_list.at(sql_info_id);
+
+      // Assert the value (int32 list) has the correct size
+      ASSERT_EQ(expected_int32_list.size(), values.value_length(i));
+
+      // For each element on current ListScalar, assert it matches with the argument
+      for (size_t j = 0; j < expected_int32_list.size(); j++) {
+        ASSERT_OK_AND_ASSIGN(auto list_item_scalar,
+                             values.values()->GetScalar(values.value_offset(i) + j));
+        const auto& list_item = checked_cast<const Int32Scalar&>(*list_item_scalar).value;
+        ASSERT_EQ(expected_int32_list[j], list_item);
+      }
+    }
+  }
+
+  explicit SqlInfoDenseUnionValidator(const DenseUnionScalar& data) : data(data) {}
+
+  SqlInfoDenseUnionValidator(const SqlInfoDenseUnionValidator&) = delete;
+  SqlInfoDenseUnionValidator(SqlInfoDenseUnionValidator&&) = delete;
+  SqlInfoDenseUnionValidator& operator=(const SqlInfoDenseUnionValidator&) = delete;
+};
+
+class TestFlightSqlServer : public ::testing::Test {
+ public:
+  std::unique_ptr<FlightSqlClient> sql_client;
+
+  arrow::Result<int64_t> ExecuteCountQuery(const std::string& query) {
+    ARROW_ASSIGN_OR_RAISE(auto flight_info, sql_client->Execute({}, query));
+
+    ARROW_ASSIGN_OR_RAISE(auto stream,
+                          sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+    std::shared_ptr<Table> table;
+    ARROW_RETURN_NOT_OK(stream->ReadAll(&table));
+
+    const std::shared_ptr<Array>& result_array = table->column(0)->chunk(0);
+    ARROW_ASSIGN_OR_RAISE(auto count_scalar, result_array->GetScalar(0));
+
+    return reinterpret_cast<Int64Scalar&>(*count_scalar).value;
+  }
+
+ protected:
+  void SetUp() override {
+    port = GetListenPort();
+    server_thread.reset(new std::thread([&]() { RunServer(); }));
+
+    std::unique_lock<std::mutex> lk(server_ready_m);
+    server_ready_cv.wait(lk);
+
+    std::stringstream ss;
+    ss << "grpc://localhost:" << port;
+    std::string uri = ss.str();
+
+    std::unique_ptr<FlightClient> client;
+    Location location;
+    ASSERT_OK(Location::Parse(uri, &location));
+    ASSERT_OK(FlightClient::Connect(location, &client));
+
+    sql_client.reset(new FlightSqlClient(std::move(client)));
+  }
+
+  void TearDown() override {
+    sql_client.reset();
+
+    ASSERT_OK(server->Shutdown());
+    server_thread->join();
+    server_thread.reset();
+  }
+
+ private:
+  int port;
+  std::shared_ptr<arrow::flight::sql::example::SQLiteFlightSqlServer> server;
+  std::unique_ptr<std::thread> server_thread;
+  std::condition_variable server_ready_cv;
+  std::mutex server_ready_m;
+
+  void RunServer() {
+    arrow::flight::Location location;
+    ARROW_CHECK_OK(arrow::flight::Location::ForGrpcTcp("localhost", port, &location));
+    arrow::flight::FlightServerOptions options(location);
+
+    ARROW_CHECK_OK(example::SQLiteFlightSqlServer::Create().Value(&server));
+
+    ARROW_CHECK_OK(server->Init(options));
+    // Exit with a clean error code (0) on SIGTERM
+    ARROW_CHECK_OK(server->SetShutdownOnSignals({SIGTERM}));
+
+    server_ready_cv.notify_all();
+    ARROW_CHECK_OK(server->Serve());
+  }
+};
+
+TEST_F(TestFlightSqlServer, TestCommandStatementQuery) {
+  ASSERT_OK_AND_ASSIGN(auto flight_info,
+                       sql_client->Execute({}, "SELECT * FROM intTable"));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const std::shared_ptr<Schema>& expected_schema =
+      arrow::schema({arrow::field("id", int64()), arrow::field("keyName", utf8()),
+                     arrow::field("value", int64()), arrow::field("foreignId", int64())});
+
+  const auto id_array = ArrayFromJSON(int64(), R"([1, 2, 3, 4])");
+  const auto keyname_array =
+      ArrayFromJSON(utf8(), R"(["one", "zero", "negative one", null])");
+  const auto value_array = ArrayFromJSON(int64(), R"([1, 0, -1, null])");
+  const auto foreignId_array = ArrayFromJSON(int64(), R"([1, 1, 1, null])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      expected_schema, {id_array, keyname_array, value_array, foreignId_array});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTables) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  std::string* table_filter_pattern = nullptr;
+  bool include_schema = false;
+  std::vector<std::string>* table_types = nullptr;
+
+  ASSERT_OK_AND_ASSIGN(
+      auto flight_info,
+      sql_client->GetTables(options, catalog, schema_filter_pattern, table_filter_pattern,
+                            include_schema, table_types));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  ASSERT_OK_AND_ASSIGN(auto catalog_name, MakeArrayOfNull(utf8(), 3))
+  ASSERT_OK_AND_ASSIGN(auto schema_name, MakeArrayOfNull(utf8(), 3))
+
+  const auto table_name =
+      ArrayFromJSON(utf8(), R"(["foreignTable", "intTable", "sqlite_sequence"])");
+  const auto table_type = ArrayFromJSON(utf8(), R"(["table", "table", "table"])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      SqlSchema::GetTablesSchema(), {catalog_name, schema_name, table_name, table_type});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTablesWithTableFilter) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  std::string table_filter_pattern = "int%";
+  bool include_schema = false;
+  std::vector<std::string>* table_types = nullptr;
+
+  ASSERT_OK_AND_ASSIGN(
+      auto flight_info,
+      sql_client->GetTables(options, catalog, schema_filter_pattern,
+                            &table_filter_pattern, include_schema, table_types));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto table_type = ArrayFromJSON(utf8(), R"(["table"])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      SqlSchema::GetTablesSchema(), {catalog_name, schema_name, table_name, table_type});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTablesWithTableTypesFilter) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  std::string* table_filter_pattern = nullptr;
+  bool include_schema = false;
+  std::vector<std::string> table_types{"index"};
+
+  ASSERT_OK_AND_ASSIGN(
+      auto flight_info,
+      sql_client->GetTables(options, catalog, schema_filter_pattern, table_filter_pattern,
+                            include_schema, &table_types));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  AssertSchemaEqual(SqlSchema::GetTablesSchema(), table->schema());
+
+  ASSERT_EQ(table->num_rows(), 0);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTablesWithUnexistenceTableTypeFilter) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  std::string* table_filter_pattern = nullptr;
+  bool include_schema = false;
+  std::vector<std::string> table_types{"table"};
+
+  ASSERT_OK_AND_ASSIGN(
+      auto flight_info,
+      sql_client->GetTables(options, catalog, schema_filter_pattern, table_filter_pattern,
+                            include_schema, &table_types));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto catalog_name = ArrayFromJSON(utf8(), R"([null, null, null])");
+  const auto schema_name = ArrayFromJSON(utf8(), R"([null, null, null])");
+  const auto table_name =
+      ArrayFromJSON(utf8(), R"(["foreignTable", "intTable", "sqlite_sequence"])");
+  const auto table_type = ArrayFromJSON(utf8(), R"(["table", "table", "table"])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      SqlSchema::GetTablesSchema(), {catalog_name, schema_name, table_name, table_type});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTablesWithIncludedSchemas) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  std::string table_filter_pattern = "int%";
+  bool include_schema = true;
+  std::vector<std::string>* table_types = nullptr;
+
+  ASSERT_OK_AND_ASSIGN(
+      auto flight_info,
+      sql_client->GetTables(options, catalog, schema_filter_pattern,
+                            &table_filter_pattern, include_schema, table_types));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto table_type = ArrayFromJSON(utf8(), R"(["table"])");
+
+  const std::shared_ptr<Schema> schema_table = arrow::schema(
+      {arrow::field("id", int64(), true), arrow::field("keyName", utf8(), true),
+       arrow::field("value", int64(), true), arrow::field("foreignId", int64(), true)});
+
+  ASSERT_OK_AND_ASSIGN(auto schema_buffer, ipc::SerializeSchema(*schema_table));
+
+  std::shared_ptr<Array> table_schema;
+  ArrayFromVector<BinaryType, std::string>({schema_buffer->ToString()}, &table_schema);
+
+  const std::shared_ptr<Table>& expected_table =
+      Table::Make(SqlSchema::GetTablesSchemaWithIncludedSchema(),
+                  {catalog_name, schema_name, table_name, table_type, table_schema});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetCatalogs) {
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetCatalogs({}));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const std::shared_ptr<Schema>& expected_schema = SqlSchema::GetCatalogsSchema();
+
+  AssertSchemaEqual(expected_schema, table->schema());
+  ASSERT_EQ(0, table->num_rows());
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetDbSchemas) {
+  FlightCallOptions options = {};
+  std::string* catalog = nullptr;
+  std::string* schema_filter_pattern = nullptr;
+  ASSERT_OK_AND_ASSIGN(auto flight_info,
+                       sql_client->GetDbSchemas(options, catalog, schema_filter_pattern));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const std::shared_ptr<Schema>& expected_schema = SqlSchema::GetDbSchemasSchema();
+
+  AssertSchemaEqual(expected_schema, table->schema());
+  ASSERT_EQ(0, table->num_rows());
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetTableTypes) {
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetTableTypes({}));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto table_type = ArrayFromJSON(utf8(), R"(["table"])");
+
+  const std::shared_ptr<Table>& expected_table =
+      Table::Make(SqlSchema::GetTableTypesSchema(), {table_type});
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandStatementUpdate) {
+  int64_t result;
+  ASSERT_OK_AND_ASSIGN(result,
+                       sql_client->ExecuteUpdate(
+                           {},
+                           "INSERT INTO intTable (keyName, value) VALUES "
+                           "('KEYNAME1', 1001), ('KEYNAME2', 1002), ('KEYNAME3', 1003)"));
+  ASSERT_EQ(3, result);
+
+  ASSERT_OK_AND_ASSIGN(result, sql_client->ExecuteUpdate(
+                                   {},
+                                   "UPDATE intTable SET keyName = 'KEYNAME1' "
+                                   "WHERE keyName = 'KEYNAME2' OR keyName = 'KEYNAME3'"));
+  ASSERT_EQ(2, result);
+
+  ASSERT_OK_AND_ASSIGN(
+      result,
+      sql_client->ExecuteUpdate({}, "DELETE FROM intTable WHERE keyName = 'KEYNAME1'"));
+  ASSERT_EQ(3, result);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandPreparedStatementQuery) {
+  ASSERT_OK_AND_ASSIGN(auto prepared_statement,
+                       sql_client->Prepare({}, "SELECT * FROM intTable"));
+
+  ASSERT_OK_AND_ASSIGN(auto flight_info, prepared_statement->Execute());
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const std::shared_ptr<Schema>& expected_schema =
+      arrow::schema({arrow::field("id", int64()), arrow::field("keyName", utf8()),
+                     arrow::field("value", int64()), arrow::field("foreignId", int64())});
+
+  const auto id_array = ArrayFromJSON(int64(), R"([1, 2, 3, 4])");
+  const auto keyname_array =
+      ArrayFromJSON(utf8(), R"(["one", "zero", "negative one", null])");
+  const auto value_array = ArrayFromJSON(int64(), R"([1, 0, -1, null])");
+  const auto foreignId_array = ArrayFromJSON(int64(), R"([1, 1, 1, null])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      expected_schema, {id_array, keyname_array, value_array, foreignId_array});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandPreparedStatementQueryWithParameterBinding) {
+  ASSERT_OK_AND_ASSIGN(
+      auto prepared_statement,
+      sql_client->Prepare({}, "SELECT * FROM intTable WHERE keyName LIKE ?"));
+
+  auto parameter_schema = prepared_statement->parameter_schema();
+
+  const std::shared_ptr<Schema>& expected_parameter_schema =
+      arrow::schema({arrow::field("parameter_1", example::GetUnknownColumnDataType())});
+
+  AssertSchemaEqual(expected_parameter_schema, parameter_schema);
+
+  std::shared_ptr<Array> type_ids = ArrayFromJSON(int8(), R"([0])");
+  std::shared_ptr<Array> offsets = ArrayFromJSON(int32(), R"([0])");
+  std::shared_ptr<Array> string_array = ArrayFromJSON(utf8(), R"(["%one"])");
+  std::shared_ptr<Array> bytes_array = ArrayFromJSON(binary(), R"([])");
+  std::shared_ptr<Array> bigint_array = ArrayFromJSON(int64(), R"([])");
+  std::shared_ptr<Array> double_array = ArrayFromJSON(float64(), R"([])");
+
+  ASSERT_OK_AND_ASSIGN(
+      auto parameter_1_array,
+      DenseUnionArray::Make(*type_ids, *offsets,
+                            {string_array, bytes_array, bigint_array, double_array},
+                            {"string", "bytes", "bigint", "double"}, {0, 1, 2, 3}));
+
+  const std::shared_ptr<RecordBatch>& record_batch =
+      RecordBatch::Make(parameter_schema, 1, {parameter_1_array});
+
+  ASSERT_OK(prepared_statement->SetParameters(record_batch));
+
+  ASSERT_OK_AND_ASSIGN(auto flight_info, prepared_statement->Execute());
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const std::shared_ptr<Schema>& expected_schema =
+      arrow::schema({arrow::field("id", int64()), arrow::field("keyName", utf8()),
+                     arrow::field("value", int64()), arrow::field("foreignId", int64())});
+
+  const auto id_array = ArrayFromJSON(int64(), R"([1, 3])");
+  const auto keyname_array = ArrayFromJSON(utf8(), R"(["one", "negative one"])");
+  const auto value_array = ArrayFromJSON(int64(), R"([1, -1])");
+  const auto foreignId_array = ArrayFromJSON(int64(), R"([1, 1])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      expected_schema, {id_array, keyname_array, value_array, foreignId_array});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandPreparedStatementUpdateWithParameterBinding) {
+  ASSERT_OK_AND_ASSIGN(
+      auto prepared_statement,
+      sql_client->Prepare(
+          {}, "INSERT INTO INTTABLE (keyName, value) VALUES ('new_value', ?)"));
+
+  auto parameter_schema = prepared_statement->parameter_schema();
+
+  const std::shared_ptr<Schema>& expected_parameter_schema =
+      arrow::schema({arrow::field("parameter_1", example::GetUnknownColumnDataType())});
+
+  AssertSchemaEqual(expected_parameter_schema, parameter_schema);
+
+  std::shared_ptr<Array> type_ids = ArrayFromJSON(int8(), R"([2])");
+  std::shared_ptr<Array> offsets = ArrayFromJSON(int32(), R"([0])");
+  std::shared_ptr<Array> string_array = ArrayFromJSON(utf8(), R"([])");
+  std::shared_ptr<Array> bytes_array = ArrayFromJSON(binary(), R"([])");
+  std::shared_ptr<Array> bigint_array = ArrayFromJSON(int64(), R"([999])");
+  std::shared_ptr<Array> double_array = ArrayFromJSON(float64(), R"([])");
+
+  ASSERT_OK_AND_ASSIGN(
+      auto parameter_1_array,
+      DenseUnionArray::Make(*type_ids, *offsets,
+                            {string_array, bytes_array, bigint_array, double_array},
+                            {"string", "bytes", "bigint", "double"}, {0, 1, 2, 3}));
+
+  const std::shared_ptr<RecordBatch>& record_batch =
+      RecordBatch::Make(parameter_schema, 1, {parameter_1_array});
+
+  ASSERT_OK(prepared_statement->SetParameters(record_batch));
+
+  ASSERT_OK_AND_EQ(4, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+
+  ASSERT_OK_AND_EQ(1, prepared_statement->ExecuteUpdate());
+
+  ASSERT_OK_AND_EQ(5, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+
+  ASSERT_OK_AND_EQ(1, sql_client->ExecuteUpdate(
+                          {}, "DELETE FROM intTable WHERE keyName = 'new_value'"));
+
+  ASSERT_OK_AND_EQ(4, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+}
+
+TEST_F(TestFlightSqlServer, TestCommandPreparedStatementUpdate) {
+  ASSERT_OK_AND_ASSIGN(
+      auto prepared_statement,
+      sql_client->Prepare(
+          {}, "INSERT INTO INTTABLE (keyName, value) VALUES ('new_value', 999)"));
+
+  ASSERT_OK_AND_EQ(4, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+
+  ASSERT_OK_AND_EQ(1, prepared_statement->ExecuteUpdate());
+
+  ASSERT_OK_AND_EQ(5, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+
+  ASSERT_OK_AND_EQ(1, sql_client->ExecuteUpdate(
+                          {}, "DELETE FROM intTable WHERE keyName = 'new_value'"));
+
+  ASSERT_OK_AND_EQ(4, ExecuteCountQuery("SELECT COUNT(*) FROM intTable"));
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetPrimaryKeys) {
+  FlightCallOptions options = {};
+  TableRef table_ref = {util::nullopt, util::nullopt, "int%"};
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetPrimaryKeys(options, table_ref));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto column_name = ArrayFromJSON(utf8(), R"(["id"])");
+  const auto key_sequence = ArrayFromJSON(int64(), R"([1])");
+
+  const std::shared_ptr<Table>& expected_table = Table::Make(
+      SqlSchema::GetPrimaryKeysSchema(),
+      {catalog_name, schema_name, table_name, column_name, key_sequence, key_name});
+
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetImportedKeys) {
+  FlightCallOptions options = {};
+  TableRef table_ref = {util::nullopt, util::nullopt, "intTable"};
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetImportedKeys(options, table_ref));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto pk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_table_name = ArrayFromJSON(utf8(), R"(["foreignTable"])");
+  const auto pk_column_name = ArrayFromJSON(utf8(), R"(["id"])");
+  const auto fk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto fk_column_name = ArrayFromJSON(utf8(), R"(["foreignId"])");
+  const auto key_sequence = ArrayFromJSON(int32(), R"([0])");
+  const auto fk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto update_rule = ArrayFromJSON(uint8(), R"([3])");
+  const auto delete_rule = ArrayFromJSON(uint8(), R"([3])");
+
+  const std::shared_ptr<Table>& expected_table =
+      Table::Make(SqlSchema::GetImportedKeysSchema(),
+                  {pk_catalog_name, pk_schema_name, pk_table_name, pk_column_name,
+                   fk_catalog_name, fk_schema_name, fk_table_name, fk_column_name,
+                   key_sequence, fk_key_name, pk_key_name, update_rule, delete_rule});
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetExportedKeys) {
+  FlightCallOptions options = {};
+  TableRef table_ref = {util::nullopt, util::nullopt, "foreignTable"};
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetExportedKeys(options, table_ref));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto pk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_table_name = ArrayFromJSON(utf8(), R"(["foreignTable"])");
+  const auto pk_column_name = ArrayFromJSON(utf8(), R"(["id"])");
+  const auto fk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto fk_column_name = ArrayFromJSON(utf8(), R"(["foreignId"])");
+  const auto key_sequence = ArrayFromJSON(int32(), R"([0])");
+  const auto fk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto update_rule = ArrayFromJSON(uint8(), R"([3])");
+  const auto delete_rule = ArrayFromJSON(uint8(), R"([3])");
+
+  const std::shared_ptr<Table>& expected_table =
+      Table::Make(SqlSchema::GetExportedKeysSchema(),
+                  {pk_catalog_name, pk_schema_name, pk_table_name, pk_column_name,
+                   fk_catalog_name, fk_schema_name, fk_table_name, fk_column_name,
+                   key_sequence, fk_key_name, pk_key_name, update_rule, delete_rule});
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetCrossReference) {
+  FlightCallOptions options = {};
+  TableRef pk_table_ref = {util::nullopt, util::nullopt, "foreignTable"};
+  TableRef fk_table_ref = {util::nullopt, util::nullopt, "intTable"};
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetCrossReference(
+                                             options, pk_table_ref, fk_table_ref));
+
+  ASSERT_OK_AND_ASSIGN(auto stream,
+                       sql_client->DoGet({}, flight_info->endpoints()[0].ticket));
+
+  std::shared_ptr<Table> table;
+  ASSERT_OK(stream->ReadAll(&table));
+
+  const auto pk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_table_name = ArrayFromJSON(utf8(), R"(["foreignTable"])");
+  const auto pk_column_name = ArrayFromJSON(utf8(), R"(["id"])");
+  const auto fk_catalog_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_schema_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto fk_table_name = ArrayFromJSON(utf8(), R"(["intTable"])");
+  const auto fk_column_name = ArrayFromJSON(utf8(), R"(["foreignId"])");
+  const auto key_sequence = ArrayFromJSON(int32(), R"([0])");
+  const auto fk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto pk_key_name = ArrayFromJSON(utf8(), R"([null])");
+  const auto update_rule = ArrayFromJSON(uint8(), R"([3])");
+  const auto delete_rule = ArrayFromJSON(uint8(), R"([3])");
+
+  const std::shared_ptr<Table>& expected_table =
+      Table::Make(SqlSchema::GetCrossReferenceSchema(),
+                  {pk_catalog_name, pk_schema_name, pk_table_name, pk_column_name,
+                   fk_catalog_name, fk_schema_name, fk_table_name, fk_column_name,
+                   key_sequence, fk_key_name, pk_key_name, update_rule, delete_rule});
+  AssertTablesEqual(*expected_table, *table);
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetSqlInfo) {
+  const auto& sql_info_expected_results = sql::example::GetSqlInfoResultMap();
+  std::vector<int> sql_info_ids;
+  sql_info_ids.reserve(sql_info_expected_results.size());
+  for (const auto& sql_info_expected_result : sql_info_expected_results) {
+    sql_info_ids.push_back(sql_info_expected_result.first);
+  }
+
+  FlightCallOptions call_options;
+  ASSERT_OK_AND_ASSIGN(auto flight_info,
+                       sql_client->GetSqlInfo(call_options, sql_info_ids));
+  ASSERT_OK_AND_ASSIGN(
+      auto reader, sql_client->DoGet(call_options, flight_info->endpoints()[0].ticket));
+  std::shared_ptr<Table> results;
+  ASSERT_OK(reader->ReadAll(&results));
+  ASSERT_EQ(2, results->num_columns());
+  ASSERT_EQ(sql_info_ids.size(), results->num_rows());
+  const auto& col_name = results->column(0);
+  const auto& col_value = results->column(1);
+  for (int32_t i = 0; i < col_name->num_chunks(); i++) {
+    const auto* col_name_chunk_data =
+        col_name->chunk(i)->data()->GetValuesSafe<int32_t>(1);
+    const auto& col_value_chunk = col_value->chunk(i);
+    for (int64_t row = 0; row < col_value->length(); row++) {
+      ASSERT_OK_AND_ASSIGN(const auto& scalar, col_value_chunk->GetScalar(row));
+      const SqlInfoDenseUnionValidator validator(
+          reinterpret_cast<const DenseUnionScalar&>(*scalar));
+      const auto& expected_result =
+          sql_info_expected_results.at(col_name_chunk_data[row]);
+      arrow::util::visit(validator, expected_result);
+    }
+  }
+}
+
+TEST_F(TestFlightSqlServer, TestCommandGetSqlInfoNoInfo) {
+  FlightCallOptions call_options;
+  ASSERT_OK_AND_ASSIGN(auto flight_info, sql_client->GetSqlInfo(call_options, {999999}));
+
+  EXPECT_RAISES_WITH_MESSAGE_THAT(
+      KeyError, ::testing::HasSubstr("No information for SQL info number 999999."),
+      sql_client->DoGet(call_options, flight_info->endpoints()[0].ticket));
+}
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/sql_info_internal.cc b/cpp/src/arrow/flight/sql/sql_info_internal.cc
new file mode 100644
index 0000000..74718fb
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/sql_info_internal.cc
@@ -0,0 +1,101 @@
+// 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.
+
+#include "arrow/flight/sql/sql_info_internal.h"
+
+#include "arrow/buffer.h"
+#include "arrow/builder.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace internal {
+
+Status SqlInfoResultAppender::operator()(const std::string& value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kStringValueIndex));
+  ARROW_RETURN_NOT_OK(string_value_builder_->Append(value));
+  return Status::OK();
+}
+
+Status SqlInfoResultAppender::operator()(const bool value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kBoolValueIndex));
+  ARROW_RETURN_NOT_OK(bool_value_builder_->Append(value));
+  return Status::OK();
+}
+
+Status SqlInfoResultAppender::operator()(const int64_t value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kBigIntValueIndex));
+  ARROW_RETURN_NOT_OK(bigint_value_builder_->Append(value));
+  return Status::OK();
+}
+
+Status SqlInfoResultAppender::operator()(const int32_t value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kInt32BitMaskIndex));
+  ARROW_RETURN_NOT_OK(int32_bitmask_builder_->Append(value));
+  return Status::OK();
+}
+
+Status SqlInfoResultAppender::operator()(const std::vector<std::string>& value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kStringListIndex));
+  ARROW_RETURN_NOT_OK(string_list_builder_->Append());
+  auto* string_list_child =
+      reinterpret_cast<StringBuilder*>(string_list_builder_->value_builder());
+  for (const auto& string : value) {
+    ARROW_RETURN_NOT_OK(string_list_child->Append(string));
+  }
+  return Status::OK();
+}
+
+Status SqlInfoResultAppender::operator()(
+    const std::unordered_map<int32_t, std::vector<int32_t>>& value) {
+  ARROW_RETURN_NOT_OK(value_builder_->Append(kInt32ToInt32ListIndex));
+  ARROW_RETURN_NOT_OK(int32_to_int32_list_builder_->Append());
+  for (const auto& pair : value) {
+    ARROW_RETURN_NOT_OK(
+        reinterpret_cast<Int32Builder*>(int32_to_int32_list_builder_->key_builder())
+            ->Append(pair.first));
+    auto* int32_list_builder =
+        reinterpret_cast<ListBuilder*>(int32_to_int32_list_builder_->item_builder());
+    ARROW_RETURN_NOT_OK(int32_list_builder->Append());
+    auto* int32_list_child =
+        reinterpret_cast<Int32Builder*>(int32_list_builder->value_builder());
+    for (const auto& int32 : pair.second) {
+      ARROW_RETURN_NOT_OK(int32_list_child->Append(int32));
+    }
+  }
+  return Status::OK();
+}
+
+SqlInfoResultAppender::SqlInfoResultAppender(DenseUnionBuilder* value_builder)
+    : value_builder_(value_builder),
+      string_value_builder_(
+          reinterpret_cast<StringBuilder*>(value_builder_->child(kStringValueIndex))),
+      bool_value_builder_(
+          reinterpret_cast<BooleanBuilder*>(value_builder_->child(kBoolValueIndex))),
+      bigint_value_builder_(
+          reinterpret_cast<Int64Builder*>(value_builder_->child(kBigIntValueIndex))),
+      int32_bitmask_builder_(
+          reinterpret_cast<Int32Builder*>(value_builder_->child(kInt32BitMaskIndex))),
+      string_list_builder_(
+          reinterpret_cast<ListBuilder*>(value_builder_->child(kStringListIndex))),
+      int32_to_int32_list_builder_(
+          reinterpret_cast<MapBuilder*>(value_builder_->child(kInt32ToInt32ListIndex))) {}
+
+}  // namespace internal
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/sql_info_internal.h b/cpp/src/arrow/flight/sql/sql_info_internal.h
new file mode 100644
index 0000000..b18789c
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/sql_info_internal.h
@@ -0,0 +1,87 @@
+// 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.
+
+#pragma once
+
+#include "arrow/flight/sql/types.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+namespace internal {
+
+/// \brief Auxiliary class used to populate GetSqlInfo's DenseUnionArray with different
+/// data types.
+class SqlInfoResultAppender {
+ public:
+  /// \brief Append a string to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(const std::string& value);
+
+  /// \brief Append a bool to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(bool value);
+
+  /// \brief Append a int64_t to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(int64_t value);
+
+  /// \brief Append a int32_t to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(int32_t value);
+
+  /// \brief Append a string list to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(const std::vector<std::string>& value);
+
+  /// \brief Append a int32 to int32 list map to the DenseUnionBuilder.
+  /// \param[in] value Value to be appended.
+  Status operator()(const std::unordered_map<int32_t, std::vector<int32_t>>& value);
+
+  /// \brief Create a Variant visitor that appends data to given
+  /// DenseUnionBuilder. \param[in] value_builder  DenseUnionBuilder to append data to.
+  explicit SqlInfoResultAppender(DenseUnionBuilder* value_builder);
+
+  SqlInfoResultAppender(const SqlInfoResultAppender&) = delete;
+  SqlInfoResultAppender(SqlInfoResultAppender&&) = delete;
+  SqlInfoResultAppender& operator=(const SqlInfoResultAppender&) = delete;
+
+ private:
+  DenseUnionBuilder* value_builder_;
+
+  // Builders for each child on dense union
+  StringBuilder* string_value_builder_;
+  BooleanBuilder* bool_value_builder_;
+  Int64Builder* bigint_value_builder_;
+  Int32Builder* int32_bitmask_builder_;
+  ListBuilder* string_list_builder_;
+  MapBuilder* int32_to_int32_list_builder_;
+
+  enum : int8_t {
+    kStringValueIndex = 0,
+    kBoolValueIndex = 1,
+    kBigIntValueIndex = 2,
+    kInt32BitMaskIndex = 3,
+    kStringListIndex = 4,
+    kInt32ToInt32ListIndex = 5
+  };
+};
+
+}  // namespace internal
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/src/arrow/flight/sql/test_app_cli.cc b/cpp/src/arrow/flight/sql/test_app_cli.cc
new file mode 100644
index 0000000..43c37be
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/test_app_cli.cc
@@ -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.
+
+#include <gflags/gflags.h>
+
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+#include <memory>
+
+#include "arrow/array/builder_binary.h"
+#include "arrow/array/builder_primitive.h"
+#include "arrow/flight/api.h"
+#include "arrow/flight/sql/api.h"
+#include "arrow/io/memory.h"
+#include "arrow/pretty_print.h"
+#include "arrow/status.h"
+#include "arrow/table.h"
+#include "arrow/util/optional.h"
+
+using arrow::Result;
+using arrow::Schema;
+using arrow::Status;
+using arrow::flight::ClientAuthHandler;
+using arrow::flight::FlightCallOptions;
+using arrow::flight::FlightClient;
+using arrow::flight::FlightDescriptor;
+using arrow::flight::FlightEndpoint;
+using arrow::flight::FlightInfo;
+using arrow::flight::FlightStreamChunk;
+using arrow::flight::FlightStreamReader;
+using arrow::flight::Location;
+using arrow::flight::Ticket;
+using arrow::flight::sql::FlightSqlClient;
+using arrow::flight::sql::TableRef;
+
+DEFINE_string(host, "localhost", "Host to connect to");
+DEFINE_int32(port, 32010, "Port to connect to");
+DEFINE_string(username, "", "Username");
+DEFINE_string(password, "", "Password");
+
+DEFINE_string(command, "", "Method to run");
+DEFINE_string(query, "", "Query");
+DEFINE_string(catalog, "", "Catalog");
+DEFINE_string(schema, "", "Schema");
+DEFINE_string(table, "", "Table");
+
+Status PrintResultsForEndpoint(FlightSqlClient& client,
+                               const FlightCallOptions& call_options,
+                               const FlightEndpoint& endpoint) {
+  ARROW_ASSIGN_OR_RAISE(auto stream, client.DoGet(call_options, endpoint.ticket));
+
+  const arrow::Result<std::shared_ptr<Schema>>& schema = stream->GetSchema();
+  ARROW_RETURN_NOT_OK(schema);
+
+  std::cout << "Schema:" << std::endl;
+  std::cout << schema->get()->ToString() << std::endl << std::endl;
+
+  std::cout << "Results:" << std::endl;
+
+  FlightStreamChunk chunk;
+  int64_t num_rows = 0;
+
+  while (true) {
+    ARROW_RETURN_NOT_OK(stream->Next(&chunk));
+    if (chunk.data == nullptr) {
+      break;
+    }
+    std::cout << chunk.data->ToString() << std::endl;
+    num_rows += chunk.data->num_rows();
+  }
+
+  std::cout << "Total: " << num_rows << std::endl;
+
+  return Status::OK();
+}
+
+Status PrintResults(FlightSqlClient& client, const FlightCallOptions& call_options,
+                    const std::unique_ptr<FlightInfo>& info) {
+  const std::vector<FlightEndpoint>& endpoints = info->endpoints();
+
+  for (size_t i = 0; i < endpoints.size(); i++) {
+    std::cout << "Results from endpoint " << i + 1 << " of " << endpoints.size()
+              << std::endl;
+    ARROW_RETURN_NOT_OK(PrintResultsForEndpoint(client, call_options, endpoints[i]));
+  }
+
+  return Status::OK();
+}
+
+Status RunMain() {
+  std::unique_ptr<FlightClient> client;
+  Location location;
+  ARROW_RETURN_NOT_OK(Location::ForGrpcTcp(FLAGS_host, FLAGS_port, &location));
+  ARROW_RETURN_NOT_OK(FlightClient::Connect(location, &client));
+
+  FlightCallOptions call_options;
+
+  if (!FLAGS_username.empty() || !FLAGS_password.empty()) {
+    Result<std::pair<std::string, std::string>> bearer_result =
+        client->AuthenticateBasicToken({}, FLAGS_username, FLAGS_password);
+    ARROW_RETURN_NOT_OK(bearer_result);
+
+    call_options.headers.push_back(bearer_result.ValueOrDie());
+  }
+
+  FlightSqlClient sql_client(std::move(client));
+
+  if (FLAGS_command == "ExecuteUpdate") {
+    ARROW_ASSIGN_OR_RAISE(auto rows, sql_client.ExecuteUpdate(call_options, FLAGS_query));
+
+    std::cout << "Result: " << rows << std::endl;
+
+    return Status::OK();
+  }
+
+  std::unique_ptr<FlightInfo> info;
+
+  if (FLAGS_command == "Execute") {
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.Execute(call_options, FLAGS_query));
+  } else if (FLAGS_command == "GetCatalogs") {
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetCatalogs(call_options));
+  } else if (FLAGS_command == "PreparedStatementExecute") {
+    ARROW_ASSIGN_OR_RAISE(auto prepared_statement,
+                          sql_client.Prepare(call_options, FLAGS_query));
+    ARROW_ASSIGN_OR_RAISE(info, prepared_statement->Execute());
+  } else if (FLAGS_command == "PreparedStatementExecuteParameterBinding") {
+    ARROW_ASSIGN_OR_RAISE(auto prepared_statement, sql_client.Prepare({}, FLAGS_query));
+    auto parameter_schema = prepared_statement->parameter_schema();
+    auto result_set_schema = prepared_statement->dataset_schema();
+
+    std::cout << result_set_schema->ToString(false) << std::endl;
+    arrow::Int64Builder int_builder;
+    ARROW_RETURN_NOT_OK(int_builder.Append(1));
+    std::shared_ptr<arrow::Array> int_array;
+    ARROW_RETURN_NOT_OK(int_builder.Finish(&int_array));
+    std::shared_ptr<arrow::RecordBatch> result;
+    result = arrow::RecordBatch::Make(parameter_schema, 1, {int_array});
+
+    ARROW_RETURN_NOT_OK(prepared_statement->SetParameters(result));
+    ARROW_ASSIGN_OR_RAISE(info, prepared_statement->Execute());
+  } else if (FLAGS_command == "GetDbSchemas") {
+    ARROW_ASSIGN_OR_RAISE(
+        info, sql_client.GetDbSchemas(call_options, &FLAGS_catalog, &FLAGS_schema));
+  } else if (FLAGS_command == "GetTableTypes") {
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetTableTypes(call_options));
+  } else if (FLAGS_command == "GetTables") {
+    ARROW_ASSIGN_OR_RAISE(
+        info, sql_client.GetTables(call_options, &FLAGS_catalog, &FLAGS_schema,
+                                   &FLAGS_table, false, nullptr));
+  } else if (FLAGS_command == "GetExportedKeys") {
+    TableRef table_ref = {arrow::util::make_optional(FLAGS_catalog),
+                          arrow::util::make_optional(FLAGS_schema), FLAGS_table};
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetExportedKeys(call_options, table_ref));
+  } else if (FLAGS_command == "GetImportedKeys") {
+    TableRef table_ref = {arrow::util::make_optional(FLAGS_catalog),
+                          arrow::util::make_optional(FLAGS_schema), FLAGS_table};
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetImportedKeys(call_options, table_ref));
+  } else if (FLAGS_command == "GetPrimaryKeys") {
+    TableRef table_ref = {arrow::util::make_optional(FLAGS_catalog),
+                          arrow::util::make_optional(FLAGS_schema), FLAGS_table};
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetPrimaryKeys(call_options, table_ref));
+  } else if (FLAGS_command == "GetSqlInfo") {
+    ARROW_ASSIGN_OR_RAISE(info, sql_client.GetSqlInfo(call_options, {}));
+  }
+
+  if (info != NULLPTR &&
+      !boost::istarts_with(FLAGS_command, "PreparedStatementExecute")) {
+    return PrintResults(sql_client, call_options, info);
+  }
+
+  return Status::OK();
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  Status st = RunMain();
+  if (!st.ok()) {
+    std::cerr << st << std::endl;
+    return 1;
+  }
+  return 0;
+}
diff --git a/cpp/src/arrow/flight/sql/test_server_cli.cc b/cpp/src/arrow/flight/sql/test_server_cli.cc
new file mode 100644
index 0000000..8074ab5
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/test_server_cli.cc
@@ -0,0 +1,63 @@
+// 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.
+
+#include <gflags/gflags.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include "arrow/flight/server.h"
+#include "arrow/flight/sql/example/sqlite_server.h"
+#include "arrow/flight/test_integration.h"
+#include "arrow/flight/test_util.h"
+#include "arrow/io/test_common.h"
+#include "arrow/testing/json_integration.h"
+#include "arrow/util/logging.h"
+
+DEFINE_int32(port, 31337, "Server port to listen on");
+
+arrow::Status RunMain() {
+  arrow::flight::Location location;
+  ARROW_CHECK_OK(arrow::flight::Location::ForGrpcTcp("0.0.0.0", FLAGS_port, &location));
+  arrow::flight::FlightServerOptions options(location);
+
+  std::shared_ptr<arrow::flight::sql::example::SQLiteFlightSqlServer> server;
+  ARROW_ASSIGN_OR_RAISE(server,
+                        arrow::flight::sql::example::SQLiteFlightSqlServer::Create())
+
+  ARROW_CHECK_OK(server->Init(options));
+  // Exit with a clean error code (0) on SIGTERM
+  ARROW_CHECK_OK(server->SetShutdownOnSignals({SIGTERM}));
+
+  std::cout << "Server listening on localhost:" << server->port() << std::endl;
+  ARROW_CHECK_OK(server->Serve());
+
+  return arrow::Status::OK();
+}
+
+int main(int argc, char** argv) {
+  gflags::SetUsageMessage("Integration testing server for Flight SQL.");
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  arrow::Status st = RunMain();
+  if (!st.ok()) {
+    std::cerr << st << std::endl;
+    return 1;
+  }
+  return 0;
+}
diff --git a/cpp/src/arrow/flight/sql/types.h b/cpp/src/arrow/flight/sql/types.h
new file mode 100644
index 0000000..44b8bca
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/types.h
@@ -0,0 +1,890 @@
+// 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.
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "arrow/type_fwd.h"
+#include "arrow/util/optional.h"
+#include "arrow/util/variant.h"
+
+namespace arrow {
+namespace flight {
+namespace sql {
+
+/// \brief Variant supporting all possible types on SQL info.
+using SqlInfoResult =
+    arrow::util::Variant<std::string, bool, int64_t, int32_t, std::vector<std::string>,
+                         std::unordered_map<int32_t, std::vector<int32_t>>>;
+
+/// \brief Map SQL info identifier to its value.
+using SqlInfoResultMap = std::unordered_map<int32_t, SqlInfoResult>;
+
+/// \brief Options to be set in the SqlInfo.
+struct SqlInfoOptions {
+  enum SqlInfo {
+    // Server Information [0-500): Provides basic information about the Flight SQL Server.
+
+    // Retrieves a UTF-8 string with the name of the Flight SQL Server.
+    FLIGHT_SQL_SERVER_NAME = 0,
+
+    // Retrieves a UTF-8 string with the native version of the Flight SQL Server.
+    FLIGHT_SQL_SERVER_VERSION = 1,
+
+    // Retrieves a UTF-8 string with the Arrow format version of the Flight SQL Server.
+    FLIGHT_SQL_SERVER_ARROW_VERSION = 2,
+
+    /*
+     * Retrieves a boolean value indicating whether the Flight SQL Server is read only.
+     *
+     * Returns:
+     * - false: if read-write
+     * - true: if read only
+     */
+    FLIGHT_SQL_SERVER_READ_ONLY = 3,
+
+    // SQL Syntax Information [500-1000): provides information about SQL syntax supported
+    // by the Flight SQL Server.
+
+    /*
+     * Retrieves a boolean value indicating whether the Flight SQL Server supports CREATE
+     * and DROP of catalogs.
+     *
+     * Returns:
+     * - false: if it doesn't support CREATE and DROP of catalogs.
+     * - true: if it supports CREATE and DROP of catalogs.
+     */
+    SQL_DDL_CATALOG = 500,
+
+    /*
+     * Retrieves a boolean value indicating whether the Flight SQL Server supports CREATE
+     * and DROP of schemas.
+     *
+     * Returns:
+     * - false: if it doesn't support CREATE and DROP of schemas.
+     * - true: if it supports CREATE and DROP of schemas.
+     */
+    SQL_DDL_SCHEMA = 501,
+
+    /*
+     * Indicates whether the Flight SQL Server supports CREATE and DROP of tables.
+     *
+     * Returns:
+     * - false: if it doesn't support CREATE and DROP of tables.
+     * - true: if it supports CREATE and DROP of tables.
+     */
+    SQL_DDL_TABLE = 502,
+
+    /*
+     * Retrieves a uint32 value representing the enu uint32 ordinal for the case
+     * sensitivity of catalog, table and schema names.
+     *
+     * The possible values are listed in
+     * `arrow.flight.protocol.sql.SqlSupportedCaseSensitivity`.
+     */
+    SQL_IDENTIFIER_CASE = 503,
+
+    // Retrieves a UTF-8 string with the supported character(s) used to surround a
+    // delimited identifier.
+    SQL_IDENTIFIER_QUOTE_CHAR = 504,
+
+    /*
+     * Retrieves a uint32 value representing the enu uint32 ordinal for the case
+     * sensitivity of quoted identifiers.
+     *
+     * The possible values are listed in
+     * `arrow.flight.protocol.sql.SqlSupportedCaseSensitivity`.
+     */
+    SQL_QUOTED_IDENTIFIER_CASE = 505,
+
+    /*
+     * Retrieves a boolean value indicating whether all tables are selectable.
+     *
+     * Returns:
+     * - false: if not all tables are selectable or if none are;
+     * - true: if all tables are selectable.
+     */
+    SQL_ALL_TABLES_ARE_SELECTABLE = 506,
+
+    /*
+     * Retrieves the null ordering.
+     *
+     * Returns a uint32 ordinal for the null ordering being used, as described in
+     * `arrow.flight.protocol.sql.SqlNullOrdering`.
+     */
+    SQL_NULL_ORDERING = 507,
+
+    // Retrieves a UTF-8 string list with values of the supported keywords.
+    SQL_KEYWORDS = 508,
+
+    // Retrieves a UTF-8 string list with values of the supported numeric functions.
+    SQL_NUMERIC_FUNCTIONS = 509,
+
+    // Retrieves a UTF-8 string list with values of the supported string functions.
+    SQL_STRING_FUNCTIONS = 510,
+
+    // Retrieves a UTF-8 string list with values of the supported system functions.
+    SQL_SYSTEM_FUNCTIONS = 511,
+
+    // Retrieves a UTF-8 string list with values of the supported datetime functions.
+    SQL_DATETIME_FUNCTIONS = 512,
+
+    /*
+     * Retrieves the UTF-8 string that can be used to escape wildcard characters.
+     * This is the string that can be used to escape '_' or '%' in the catalog search
+     * parameters that are a pattern (and therefore use one of the wildcard characters).
+     * The '_' character represents any single character; the '%' character represents any
+     * sequence of zero or more characters.
+     */
+    SQL_SEARCH_STRING_ESCAPE = 513,
+
+    /*
+     * Retrieves a UTF-8 string with all the "extra" characters that can be used in
+     * unquoted identifier names (those beyond a-z, A-Z, 0-9 and _).
+     */
+    SQL_EXTRA_NAME_CHARACTERS = 514,
+
+    /*
+     * Retrieves a boolean value indicating whether column aliasing is supported.
+     * If so, the SQL AS clause can be used to provide names for computed columns or to
+     * provide alias names for columns as required.
+     *
+     * Returns:
+     * - false: if column aliasing is unsupported;
+     * - true: if column aliasing is supported.
+     */
+    SQL_SUPPORTS_COLUMN_ALIASING = 515,
+
+    /*
+     * Retrieves a boolean value indicating whether concatenations between null and
+     * non-null values being null are supported.
+     *
+     * - Returns:
+     * - false: if concatenations between null and non-null values being null are
+     * unsupported;
+     * - true: if concatenations between null and non-null values being null are
+     * supported.
+     */
+    SQL_NULL_PLUS_NULL_IS_NULL = 516,
+
+    /*
+     * Retrieves a map where the key is the type to convert from and the value is a list
+     * with the types to convert to, indicating the supported conversions. Each key and
+     * each item on the list value is a value to a predefined type on SqlSupportsConvert
+     * enum. The returned map will be:  map<int32, list<int32>>
+     */
+    SQL_SUPPORTS_CONVERT = 517,
+
+    /*
+     * Retrieves a boolean value indicating whether, when table correlation names are
+     * supported, they are restricted to being different from the names of the tables.
+     *
+     * Returns:
+     * - false: if table correlation names are unsupported;
+     * - true: if table correlation names are supported.
+     */
+    SQL_SUPPORTS_TABLE_CORRELATION_NAMES = 518,
+
+    /*
+     * Retrieves a boolean value indicating whether, when table correlation names are
+     * supported, they are restricted to being different from the names of the tables.
+     *
+     * Returns:
+     * - false: if different table correlation names are unsupported;
+     * - true: if different table correlation names are supported
+     */
+    SQL_SUPPORTS_DIFFERENT_TABLE_CORRELATION_NAMES = 519,
+
+    /*
+     * Retrieves a boolean value indicating whether expressions in ORDER BY lists are
+     * supported.
+     *
+     * Returns:
+     * - false: if expressions in ORDER BY are unsupported;
+     * - true: if expressions in ORDER BY are supported;
+     */
+    SQL_SUPPORTS_EXPRESSIONS_IN_ORDER_BY = 520,
+
+    /*
+     * Retrieves a boolean value indicating whether using a column that is not in the
+     * SELECT statement in a GROUP BY clause is supported.
+     *
+     * Returns:
+     * - false: if using a column that is not in the SELECT statement in a GROUP BY clause
+     * is unsupported;
+     * - true: if using a column that is not in the SELECT statement in a GROUP BY clause
+     * is supported.
+     */
+    SQL_SUPPORTS_ORDER_BY_UNRELATED = 521,
+
+    /*
+     * Retrieves the supported GROUP BY commands;
+     *
+     * Returns an int32 bitmask value representing the supported commands.
+     * The returned bitmask should be parsed in order to retrieve the supported commands.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (GROUP BY is unsupported);
+     * - return 1 (\b1)   => [SQL_GROUP_BY_UNRELATED];
+     * - return 2 (\b10)  => [SQL_GROUP_BY_BEYOND_SELECT];
+     * - return 3 (\b11)  => [SQL_GROUP_BY_UNRELATED, SQL_GROUP_BY_BEYOND_SELECT].
+     * Valid GROUP BY types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedGroupBy`.
+     */
+    SQL_SUPPORTED_GROUP_BY = 522,
+
+    /*
+     * Retrieves a boolean value indicating whether specifying a LIKE escape clause is
+     * supported.
+     *
+     * Returns:
+     * - false: if specifying a LIKE escape clause is unsupported;
+     * - true: if specifying a LIKE escape clause is supported.
+     */
+    SQL_SUPPORTS_LIKE_ESCAPE_CLAUSE = 523,
+
+    /*
+     * Retrieves a boolean value indicating whether columns may be defined as
+     * non-nullable.
+     *
+     * Returns:
+     * - false: if columns cannot be defined as non-nullable;
+     * - true: if columns may be defined as non-nullable.
+     */
+    SQL_SUPPORTS_NON_NULLABLE_COLUMNS = 524,
+
+    /*
+     * Retrieves the supported SQL grammar level as per the ODBC specification.
+     *
+     * Returns an int32 bitmask value representing the supported SQL grammar level.
+     * The returned bitmask should be parsed in order to retrieve the supported grammar
+     * levels.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (SQL grammar is unsupported);
+     * - return 1 (\b1)   => [SQL_MINIMUM_GRAMMAR];
+     * - return 2 (\b10)  => [SQL_CORE_GRAMMAR];
+     * - return 3 (\b11)  => [SQL_MINIMUM_GRAMMAR, SQL_CORE_GRAMMAR];
+     * - return 4 (\b100) => [SQL_EXTENDED_GRAMMAR];
+     * - return 5 (\b101) => [SQL_MINIMUM_GRAMMAR, SQL_EXTENDED_GRAMMAR];
+     * - return 6 (\b110) => [SQL_CORE_GRAMMAR, SQL_EXTENDED_GRAMMAR];
+     * - return 7 (\b111) => [SQL_MINIMUM_GRAMMAR, SQL_CORE_GRAMMAR,
+     * SQL_EXTENDED_GRAMMAR]. Valid SQL grammar levels are described under
+     * `arrow.flight.protocol.sql.SupportedSqlGrammar`.
+     */
+    SQL_SUPPORTED_GRAMMAR = 525,
+
+    /*
+     * Retrieves the supported ANSI92 SQL grammar level.
+     *
+     * Returns an int32 bitmask value representing the supported ANSI92 SQL grammar level.
+     * The returned bitmask should be parsed in order to retrieve the supported commands.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (ANSI92 SQL grammar is unsupported);
+     * - return 1 (\b1)   => [ANSI92_ENTRY_SQL];
+     * - return 2 (\b10)  => [ANSI92_INTERMEDIATE_SQL];
+     * - return 3 (\b11)  => [ANSI92_ENTRY_SQL, ANSI92_INTERMEDIATE_SQL];
+     * - return 4 (\b100) => [ANSI92_FULL_SQL];
+     * - return 5 (\b101) => [ANSI92_ENTRY_SQL, ANSI92_FULL_SQL];
+     * - return 6 (\b110) => [ANSI92_INTERMEDIATE_SQL, ANSI92_FULL_SQL];
+     * - return 7 (\b111) => [ANSI92_ENTRY_SQL, ANSI92_INTERMEDIATE_SQL, ANSI92_FULL_SQL].
+     * Valid ANSI92 SQL grammar levels are described under
+     * `arrow.flight.protocol.sql.SupportedAnsi92SqlGrammarLevel`.
+     */
+    SQL_ANSI92_SUPPORTED_LEVEL = 526,
+
+    /*
+     * Retrieves a boolean value indicating whether the SQL Integrity Enhancement Facility
+     * is supported.
+     *
+     * Returns:
+     * - false: if the SQL Integrity Enhancement Facility is supported;
+     * - true: if the SQL Integrity Enhancement Facility is supported.
+     */
+    SQL_SUPPORTS_INTEGRITY_ENHANCEMENT_FACILITY = 527,
+
+    /*
+     * Retrieves the support level for SQL OUTER JOINs.
+     *
+     * Returns a uint3 uint32 ordinal for the SQL ordering being used, as described in
+     * `arrow.flight.protocol.sql.SqlOuterJoinsSupportLevel`.
+     */
+    SQL_OUTER_JOINS_SUPPORT_LEVEL = 528,
+
+    // Retrieves a UTF-8 string with the preferred term for "schema".
+    SQL_SCHEMA_TERM = 529,
+
+    // Retrieves a UTF-8 string with the preferred term for "procedure".
+    SQL_PROCEDURE_TERM = 530,
+
+    // Retrieves a UTF-8 string with the preferred term for "catalog".
+    SQL_CATALOG_TERM = 531,
+
+    /*
+     * Retrieves a boolean value indicating whether a catalog appears at the start of a
+     * fully qualified table name.
+     *
+     * - false: if a catalog does not appear at the start of a fully qualified table name;
+     * - true: if a catalog appears at the start of a fully qualified table name.
+     */
+    SQL_CATALOG_AT_START = 532,
+
+    /*
+     * Retrieves the supported actions for a SQL schema.
+     *
+     * Returns an int32 bitmask value representing the supported actions for a SQL schema.
+     * The returned bitmask should be parsed in order to retrieve the supported actions
+     * for a SQL schema.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported actions for SQL schema);
+     * - return 1 (\b1)   => [SQL_ELEMENT_IN_PROCEDURE_CALLS];
+     * - return 2 (\b10)  => [SQL_ELEMENT_IN_INDEX_DEFINITIONS];
+     * - return 3 (\b11)  => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_INDEX_DEFINITIONS];
+     * - return 4 (\b100) => [SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 5 (\b101) => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 6 (\b110) => [SQL_ELEMENT_IN_INDEX_DEFINITIONS,
+     * SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 7 (\b111) => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_INDEX_DEFINITIONS, SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS]. Valid
+     * actions for a SQL schema described under
+     * `arrow.flight.protocol.sql.SqlSupportedElementActions`.
+     */
+    SQL_SCHEMAS_SUPPORTED_ACTIONS = 533,
+
+    /*
+     * Retrieves the supported actions for a SQL schema.
+     *
+     * Returns an int32 bitmask value representing the supported actions for a SQL
+     * catalog. The returned bitmask should be parsed in order to retrieve the supported
+     * actions for a SQL catalog.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported actions for SQL catalog);
+     * - return 1 (\b1)   => [SQL_ELEMENT_IN_PROCEDURE_CALLS];
+     * - return 2 (\b10)  => [SQL_ELEMENT_IN_INDEX_DEFINITIONS];
+     * - return 3 (\b11)  => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_INDEX_DEFINITIONS];
+     * - return 4 (\b100) => [SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 5 (\b101) => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 6 (\b110) => [SQL_ELEMENT_IN_INDEX_DEFINITIONS,
+     * SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS];
+     * - return 7 (\b111) => [SQL_ELEMENT_IN_PROCEDURE_CALLS,
+     * SQL_ELEMENT_IN_INDEX_DEFINITIONS, SQL_ELEMENT_IN_PRIVILEGE_DEFINITIONS]. Valid
+     * actions for a SQL catalog are described under
+     * `arrow.flight.protocol.sql.SqlSupportedElementActions`.
+     */
+    SQL_CATALOGS_SUPPORTED_ACTIONS = 534,
+
+    /*
+     * Retrieves the supported SQL positioned commands.
+     *
+     * Returns an int32 bitmask value representing the supported SQL positioned commands.
+     * The returned bitmask should be parsed in order to retrieve the supported SQL
+     * positioned commands.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported SQL positioned commands);
+     * - return 1 (\b1)   => [SQL_POSITIONED_DELETE];
+     * - return 2 (\b10)  => [SQL_POSITIONED_UPDATE];
+     * - return 3 (\b11)  => [SQL_POSITIONED_DELETE, SQL_POSITIONED_UPDATE].
+     * Valid SQL positioned commands are described under
+     * `arrow.flight.protocol.sql.SqlSupportedPositionedCommands`.
+     */
+    SQL_SUPPORTED_POSITIONED_COMMANDS = 535,
+
+    /*
+     * Retrieves a boolean value indicating whether SELECT FOR UPDATE statements are
+     * supported.
+     *
+     * Returns:
+     * - false: if SELECT FOR UPDATE statements are unsupported;
+     * - true: if SELECT FOR UPDATE statements are supported.
+     */
+    SQL_SELECT_FOR_UPDATE_SUPPORTED = 536,
+
+    /*
+     * Retrieves a boolean value indicating whether stored procedure calls that use the
+     * stored procedure escape syntax are supported.
+     *
+     * Returns:
+     * - false: if stored procedure calls that use the stored procedure escape syntax are
+     * unsupported;
+     * - true: if stored procedure calls that use the stored procedure escape syntax are
+     * supported.
+     */
+    SQL_STORED_PROCEDURES_SUPPORTED = 537,
+
+    /*
+     * Retrieves the supported SQL subqueries.
+     *
+     * Returns an int32 bitmask value representing the supported SQL subqueries.
+     * The returned bitmask should be parsed in order to retrieve the supported SQL
+     * subqueries.
+     *
+     * For instance:
+     * - return 0   (\b0)     => [] (no supported SQL subqueries);
+     * - return 1   (\b1)     => [SQL_SUBQUERIES_IN_COMPARISONS];
+     * - return 2   (\b10)    => [SQL_SUBQUERIES_IN_EXISTS];
+     * - return 3   (\b11)    => [SQL_SUBQUERIES_IN_COMPARISONS,
+     * SQL_SUBQUERIES_IN_EXISTS];
+     * - return 4   (\b100)   => [SQL_SUBQUERIES_IN_INS];
+     * - return 5   (\b101)   => [SQL_SUBQUERIES_IN_COMPARISONS, SQL_SUBQUERIES_IN_INS];
+     * - return 6   (\b110)   => [SQL_SUBQUERIES_IN_COMPARISONS,
+     * SQL_SUBQUERIES_IN_EXISTS];
+     * - return 7   (\b111)   => [SQL_SUBQUERIES_IN_COMPARISONS, SQL_SUBQUERIES_IN_EXISTS,
+     * SQL_SUBQUERIES_IN_INS];
+     * - return 8   (\b1000)  => [SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 9   (\b1001)  => [SQL_SUBQUERIES_IN_COMPARISONS,
+     * SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 10  (\b1010)  => [SQL_SUBQUERIES_IN_EXISTS,
+     * SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 11  (\b1011)  => [SQL_SUBQUERIES_IN_COMPARISONS, SQL_SUBQUERIES_IN_EXISTS,
+     * SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 12  (\b1100)  => [SQL_SUBQUERIES_IN_INS, SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 13  (\b1101)  => [SQL_SUBQUERIES_IN_COMPARISONS, SQL_SUBQUERIES_IN_INS,
+     * SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 14  (\b1110)  => [SQL_SUBQUERIES_IN_EXISTS, SQL_SUBQUERIES_IN_INS,
+     * SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - return 15  (\b1111)  => [SQL_SUBQUERIES_IN_COMPARISONS, SQL_SUBQUERIES_IN_EXISTS,
+     * SQL_SUBQUERIES_IN_INS, SQL_SUBQUERIES_IN_QUANTIFIEDS];
+     * - ...
+     * Valid SQL subqueries are described under
+     * `arrow.flight.protocol.sql.SqlSupportedSubqueries`.
+     */
+    SQL_SUPPORTED_SUBQUERIES = 538,
+
+    /*
+     * Retrieves a boolean value indicating whether correlated subqueries are supported.
+     *
+     * Returns:
+     * - false: if correlated subqueries are unsupported;
+     * - true: if correlated subqueries are supported.
+     */
+    SQL_CORRELATED_SUBQUERIES_SUPPORTED = 539,
+
+    /*
+     * Retrieves the supported SQL UNIONs.
+     *
+     * Returns an int32 bitmask value representing the supported SQL UNIONs.
+     * The returned bitmask should be parsed in order to retrieve the supported SQL
+     * UNIONs.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported SQL positioned commands);
+     * - return 1 (\b1)   => [SQL_UNION];
+     * - return 2 (\b10)  => [SQL_UNION_ALL];
+     * - return 3 (\b11)  => [SQL_UNION, SQL_UNION_ALL].
+     * Valid SQL positioned commands are described under
+     * `arrow.flight.protocol.sql.SqlSupportedUnions`.
+     */
+    SQL_SUPPORTED_UNIONS = 540,
+
+    // Retrieves a uint32 value representing the maximum number of hex characters allowed
+    // in an inline binary literal.
+    SQL_MAX_BINARY_LITERAL_LENGTH = 541,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed for
+    // a character literal.
+    SQL_MAX_CHAR_LITERAL_LENGTH = 542,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed for
+    // a column name.
+    SQL_MAX_COLUMN_NAME_LENGTH = 543,
+
+    // Retrieves a uint32 value representing the the maximum number of columns allowed in
+    // a GROUP BY clause.
+    SQL_MAX_COLUMNS_IN_GROUP_BY = 544,
+
+    // Retrieves a uint32 value representing the maximum number of columns allowed in an
+    // index.
+    SQL_MAX_COLUMNS_IN_INDEX = 545,
+
+    // Retrieves a uint32 value representing the maximum number of columns allowed in an
+    // ORDER BY clause.
+    SQL_MAX_COLUMNS_IN_ORDER_BY = 546,
+
+    // Retrieves a uint32 value representing the maximum number of columns allowed in a
+    // SELECT list.
+    SQL_MAX_COLUMNS_IN_SELECT = 547,
+
+    // Retrieves a uint32 value representing the maximum number of columns allowed in a
+    // table.
+    SQL_MAX_COLUMNS_IN_TABLE = 548,
+
+    // Retrieves a uint32 value representing the maximum number of concurrent connections
+    // possible.
+    SQL_MAX_CONNECTIONS = 549,
+
+    // Retrieves a uint32 value the maximum number of characters allowed in a cursor name.
+    SQL_MAX_CURSOR_NAME_LENGTH = 550,
+
+    /*
+     * Retrieves a uint32 value representing the maximum number of bytes allowed for an
+     * index, including all of the parts of the index.
+     */
+    SQL_MAX_INDEX_LENGTH = 551,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed in a
+    // procedure name.
+    SQL_SCHEMA_NAME_LENGTH = 552,
+
+    // Retrieves a uint32 value representing the maximum number of bytes allowed in a
+    // single row.
+    SQL_MAX_PROCEDURE_NAME_LENGTH = 553,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed in a
+    // catalog name.
+    SQL_MAX_CATALOG_NAME_LENGTH = 554,
+
+    // Retrieves a uint32 value representing the maximum number of bytes allowed in a
+    // single row.
+    SQL_MAX_ROW_SIZE = 555,
+
+    /*
+     * Retrieves a boolean indicating whether the return value for the JDBC method
+     * getMaxRowSize includes the SQL data types LONGVARCHAR and LONGVARBINARY.
+     *
+     * Returns:
+     * - false: if return value for the JDBC method getMaxRowSize does
+     *          not include the SQL data types LONGVARCHAR and LONGVARBINARY;
+     * - true: if return value for the JDBC method getMaxRowSize includes
+     *         the SQL data types LONGVARCHAR and LONGVARBINARY.
+     */
+    SQL_MAX_ROW_SIZE_INCLUDES_BLOBS = 556,
+
+    /*
+     * Retrieves a uint32 value representing the maximum number of characters allowed for
+     * an SQL statement; a result of 0 (zero) means that there is no limit or the limit is
+     * not known.
+     */
+    SQL_MAX_STATEMENT_LENGTH = 557,
+
+    // Retrieves a uint32 value representing the maximum number of active statements that
+    // can be open at the same time.
+    SQL_MAX_STATEMENTS = 558,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed in a
+    // table name.
+    SQL_MAX_TABLE_NAME_LENGTH = 559,
+
+    // Retrieves a uint32 value representing the maximum number of tables allowed in a
+    // SELECT statement.
+    SQL_MAX_TABLES_IN_SELECT = 560,
+
+    // Retrieves a uint32 value representing the maximum number of characters allowed in a
+    // user name.
+    SQL_MAX_USERNAME_LENGTH = 561,
+
+    /*
+     * Retrieves this database's default transaction isolation level as described in
+     * `arrow.flight.protocol.sql.SqlTransactionIsolationLevel`.
+     *
+     * Returns a uint32 ordinal for the SQL transaction isolation level.
+     */
+    SQL_DEFAULT_TRANSACTION_ISOLATION = 562,
+
+    /*
+     * Retrieves a boolean value indicating whether transactions are supported. If not,
+     * invoking the method commit is a noop, and the isolation level is
+     * `arrow.flight.protocol.sql.SqlTransactionIsolationLevel.TRANSACTION_NONE`.
+     *
+     * Returns:
+     * - false: if transactions are unsupported;
+     * - true: if transactions are supported.
+     */
+    SQL_TRANSACTIONS_SUPPORTED = 563,
+
+    /*
+     * Retrieves the supported transactions isolation levels.
+     *
+     * Returns an int32 bitmask value representing the supported transactions isolation
+     * levels. The returned bitmask should be parsed in order to retrieve the supported
+     * transactions isolation levels.
+     *
+     * For instance:
+     * - return 0   (\b0)     => [] (no supported SQL transactions isolation levels);
+     * - return 1   (\b1)     => [SQL_TRANSACTION_NONE];
+     * - return 2   (\b10)    => [SQL_TRANSACTION_READ_UNCOMMITTED];
+     * - return 3   (\b11)    => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_READ_UNCOMMITTED];
+     * - return 4   (\b100)   => [SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 5   (\b101)   => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 6   (\b110)   => [SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 7   (\b111)   => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 8   (\b1000)  => [SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 9   (\b1001)  => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 10  (\b1010)  => [SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 11  (\b1011)  => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 12  (\b1100)  => [SQL_TRANSACTION_REPEATABLE_READ,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 13  (\b1101)  => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_REPEATABLE_READ,
+     * SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 14  (\b1110)  => [SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ, SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 15  (\b1111)  => [SQL_TRANSACTION_NONE, SQL_TRANSACTION_READ_UNCOMMITTED,
+     * SQL_TRANSACTION_REPEATABLE_READ, SQL_TRANSACTION_REPEATABLE_READ];
+     * - return 16  (\b10000) => [SQL_TRANSACTION_SERIALIZABLE];
+     * - ...
+     * Valid SQL positioned commands are described under
+     * `arrow.flight.protocol.sql.SqlTransactionIsolationLevel`.
+     */
+    SQL_SUPPORTED_TRANSACTIONS_ISOLATION_LEVELS = 564,
+
+    /*
+     * Retrieves a boolean value indicating whether a data definition statement within a
+     * transaction forces the transaction to commit.
+     *
+     * Returns:
+     * - false: if a data definition statement within a transaction does not force the
+     * transaction to commit;
+     * - true: if a data definition statement within a transaction forces the transaction
+     * to commit.
+     */
+    SQL_DATA_DEFINITION_CAUSES_TRANSACTION_COMMIT = 565,
+
+    /*
+     * Retrieves a boolean value indicating whether a data definition statement within a
+     * transaction is ignored.
+     *
+     * Returns:
+     * - false: if a data definition statement within a transaction is taken into account;
+     * - true: a data definition statement within a transaction is ignored.
+     */
+    SQL_DATA_DEFINITIONS_IN_TRANSACTIONS_IGNORED = 566,
+
+    /*
+     * Retrieves an int32 bitmask value representing the supported result set types.
+     * The returned bitmask should be parsed in order to retrieve the supported result set
+     * types.
+     *
+     * For instance:
+     * - return 0   (\b0)     => [] (no supported result set types);
+     * - return 1   (\b1)     => [SQL_RESULT_SET_TYPE_UNSPECIFIED];
+     * - return 2   (\b10)    => [SQL_RESULT_SET_TYPE_FORWARD_ONLY];
+     * - return 3   (\b11)    => [SQL_RESULT_SET_TYPE_UNSPECIFIED,
+     * SQL_RESULT_SET_TYPE_FORWARD_ONLY];
+     * - return 4   (\b100)   => [SQL_RESULT_SET_TYPE_SCROLL_INSENSITIVE];
+     * - return 5   (\b101)   => [SQL_RESULT_SET_TYPE_UNSPECIFIED,
+     * SQL_RESULT_SET_TYPE_SCROLL_INSENSITIVE];
+     * - return 6   (\b110)   => [SQL_RESULT_SET_TYPE_FORWARD_ONLY,
+     * SQL_RESULT_SET_TYPE_SCROLL_INSENSITIVE];
+     * - return 7   (\b111)   => [SQL_RESULT_SET_TYPE_UNSPECIFIED,
+     * SQL_RESULT_SET_TYPE_FORWARD_ONLY, SQL_RESULT_SET_TYPE_SCROLL_INSENSITIVE];
+     * - return 8   (\b1000)  => [SQL_RESULT_SET_TYPE_SCROLL_SENSITIVE];
+     * - ...
+     * Valid result set types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetType`.
+     */
+    SQL_SUPPORTED_RESULT_SET_TYPES = 567,
+
+    /*
+     * Returns an int32 bitmask value concurrency types supported for
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetType.SQL_RESULT_SET_TYPE_UNSPECIFIED`.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported concurrency types for this result set type)
+     * - return 1 (\b1)   => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED]
+     * - return 2 (\b10)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 3 (\b11)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 4 (\b100) => [SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 5 (\b101) => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 6 (\b110)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 7 (\b111)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY, SQL_RESULT_SET_CONCURRENCY_UPDATABLE] Valid
+     * result set types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetConcurrency`.
+     */
+    SQL_SUPPORTED_CONCURRENCIES_FOR_RESULT_SET_UNSPECIFIED = 568,
+
+    /*
+     * Returns an int32 bitmask value concurrency types supported for
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetType.SQL_RESULT_SET_TYPE_FORWARD_ONLY`.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported concurrency types for this result set type)
+     * - return 1 (\b1)   => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED]
+     * - return 2 (\b10)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 3 (\b11)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 4 (\b100) => [SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 5 (\b101) => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 6 (\b110)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 7 (\b111)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY, SQL_RESULT_SET_CONCURRENCY_UPDATABLE] Valid
+     * result set types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetConcurrency`.
+     */
+    SQL_SUPPORTED_CONCURRENCIES_FOR_RESULT_SET_FORWARD_ONLY = 569,
+
+    /*
+     * Returns an int32 bitmask value concurrency types supported for
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetType.SQL_RESULT_SET_TYPE_SCROLL_SENSITIVE`.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported concurrency types for this result set type)
+     * - return 1 (\b1)   => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED]
+     * - return 2 (\b10)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 3 (\b11)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 4 (\b100) => [SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 5 (\b101) => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 6 (\b110)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 7 (\b111)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY, SQL_RESULT_SET_CONCURRENCY_UPDATABLE] Valid
+     * result set types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetConcurrency`.
+     */
+    SQL_SUPPORTED_CONCURRENCIES_FOR_RESULT_SET_SCROLL_SENSITIVE = 570,
+
+    /*
+     * Returns an int32 bitmask value concurrency types supported for
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetType.SQL_RESULT_SET_TYPE_SCROLL_INSENSITIVE`.
+     *
+     * For instance:
+     * - return 0 (\b0)   => [] (no supported concurrency types for this result set type)
+     * - return 1 (\b1)   => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED]
+     * - return 2 (\b10)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 3 (\b11)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY]
+     * - return 4 (\b100) => [SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 5 (\b101) => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 6 (\b110)  => [SQL_RESULT_SET_CONCURRENCY_READ_ONLY,
+     * SQL_RESULT_SET_CONCURRENCY_UPDATABLE]
+     * - return 7 (\b111)  => [SQL_RESULT_SET_CONCURRENCY_UNSPECIFIED,
+     * SQL_RESULT_SET_CONCURRENCY_READ_ONLY, SQL_RESULT_SET_CONCURRENCY_UPDATABLE] Valid
+     * result set types are described under
+     * `arrow.flight.protocol.sql.SqlSupportedResultSetConcurrency`.
+     */
+    SQL_SUPPORTED_CONCURRENCIES_FOR_RESULT_SET_SCROLL_INSENSITIVE = 571,
+
+    /*
+     * Retrieves a boolean value indicating whether this database supports batch updates.
+     *
+     * - false: if this database does not support batch updates;
+     * - true: if this database supports batch updates.
+     */
+    SQL_BATCH_UPDATES_SUPPORTED = 572,
+
+    /*
+     * Retrieves a boolean value indicating whether this database supports savepoints.
+     *
+     * Returns:
+     * - false: if this database does not support savepoints;
+     * - true: if this database supports savepoints.
+     */
+    SQL_SAVEPOINTS_SUPPORTED = 573,
+
+    /*
+     * Retrieves a boolean value indicating whether named parameters are supported in
+     * callable statements.
+     *
+     * Returns:
+     * - false: if named parameters in callable statements are unsupported;
+     * - true: if named parameters in callable statements are supported.
+     */
+    SQL_NAMED_PARAMETERS_SUPPORTED = 574,
+
+    /*
+     * Retrieves a boolean value indicating whether updates made to a LOB are made on a
+     * copy or directly to the LOB.
+     *
+     * Returns:
+     * - false: if updates made to a LOB are made directly to the LOB;
+     * - true: if updates made to a LOB are made on a copy.
+     */
+    SQL_LOCATORS_UPDATE_COPY = 575,
+
+    /*
+     * Retrieves a boolean value indicating whether invoking user-defined or vendor
+     * functions using the stored procedure escape syntax is supported.
+     *
+     * Returns:
+     * - false: if invoking user-defined or vendor functions using the stored procedure
+     * escape syntax is unsupported;
+     * - true: if invoking user-defined or vendor functions using the stored procedure
+     * escape syntax is supported.
+     */
+    SQL_STORED_FUNCTIONS_USING_CALL_SYNTAX_SUPPORTED = 576,
+  };
+
+  enum SqlSupportedCaseSensitivity {
+    SQL_CASE_SENSITIVITY_UNKNOWN = 0,
+    SQL_CASE_SENSITIVITY_CASE_INSENSITIVE = 1,
+    SQL_CASE_SENSITIVITY_UPPERCASE = 2,
+  };
+
+  enum SqlNullOrdering {
+    SQL_NULLS_SORTED_HIGH = 0,
+    SQL_NULLS_SORTED_LOW = 1,
+    SQL_NULLS_SORTED_AT_START = 2,
+    SQL_NULLS_SORTED_AT_END = 3,
+  };
+
+  enum SqlSupportsConvert {
+    SQL_CONVERT_BIGINT = 0,
+    SQL_CONVERT_BINARY = 1,
+    SQL_CONVERT_BIT = 2,
+    SQL_CONVERT_CHAR = 3,
+    SQL_CONVERT_DATE = 4,
+    SQL_CONVERT_DECIMAL = 5,
+    SQL_CONVERT_FLOAT = 6,
+    SQL_CONVERT_INTEGER = 7,
+    SQL_CONVERT_INTERVAL_DAY_TIME = 8,
+    SQL_CONVERT_INTERVAL_YEAR_MONTH = 9,
+    SQL_CONVERT_LONGVARBINARY = 10,
+    SQL_CONVERT_LONGVARCHAR = 11,
+    SQL_CONVERT_NUMERIC = 12,
+    SQL_CONVERT_REAL = 13,
+    SQL_CONVERT_SMALLINT = 14,
+    SQL_CONVERT_TIME = 15,
+    SQL_CONVERT_TIMESTAMP = 16,
+    SQL_CONVERT_TINYINT = 17,
+    SQL_CONVERT_VARBINARY = 18,
+    SQL_CONVERT_VARCHAR = 19,
+  };
+};
+
+/// \brief Table reference, optionally containing table's catalog and db_schema.
+struct TableRef {
+  util::optional<std::string> catalog;
+  util::optional<std::string> db_schema;
+  std::string table;
+};
+
+}  // namespace sql
+}  // namespace flight
+}  // namespace arrow
diff --git a/cpp/vcpkg.json b/cpp/vcpkg.json
index 64ece20..5566438 100644
--- a/cpp/vcpkg.json
+++ b/cpp/vcpkg.json
@@ -44,6 +44,7 @@
     "rapidjson",
     "re2",
     "snappy",
+    "sqlite3",
     "thrift",
     "utf8proc",
     "zlib",
diff --git a/docker-compose.yml b/docker-compose.yml
index 834304d..91f4cbf 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -269,6 +269,7 @@ services:
       ARROW_CXXFLAGS: "-Og"  # Shrink test runtime by enabling minimal optimizations
       ARROW_ENABLE_TIMING_TESTS:  # inherit
       ARROW_FLIGHT: "OFF"
+      ARROW_FLIGHT_SQL: "OFF"
       ARROW_GANDIVA: "OFF"
       ARROW_JEMALLOC: "OFF"
       ARROW_RUNTIME_SIMD_LEVEL: "AVX2"  # AVX512 not supported by Valgrind (ARROW-9851)
@@ -1022,6 +1023,7 @@ services:
     environment:
       <<: *ccache
       ARROW_FLIGHT: "OFF"
+      ARROW_FLIGHT_SQL: "OFF"
       ARROW_GANDIVA: "OFF"
     volumes: *conda-volumes
     command:
@@ -1617,6 +1619,7 @@ services:
     environment:
       <<: *ccache
       ARROW_FLIGHT: "OFF"
+      ARROW_FLIGHT_SQL: "OFF"
       ARROW_GANDIVA: "OFF"
       ARROW_PLASMA: "OFF"
       ARROW_HIVESERVER2: "ON"