diff --git a/c_glib/arrow-flight-glib/client.cpp b/c_glib/arrow-flight-glib/client.cpp index 65bc2d56a51f6..60dec29dbbdfb 100644 --- a/c_glib/arrow-flight-glib/client.cpp +++ b/c_glib/arrow-flight-glib/client.cpp @@ -148,7 +148,7 @@ gaflight_call_options_clear_headers(GAFlightCallOptions *options) * @func: (scope call): The user's callback function. * @user_data: (closure): Data for @func. * - * Iterates over all header in the options. + * Iterates over all headers in the options. * * Since: 9.0.0 */ diff --git a/c_glib/arrow-flight-glib/server.cpp b/c_glib/arrow-flight-glib/server.cpp index 53ecbf6e08db5..13debc8a60369 100644 --- a/c_glib/arrow-flight-glib/server.cpp +++ b/c_glib/arrow-flight-glib/server.cpp @@ -293,9 +293,11 @@ gaflight_message_reader_get_descriptor(GAFlightMessageReader *reader) } -typedef struct GAFlightServerCallContextPrivate_ { +struct GAFlightServerCallContextPrivate { arrow::flight::ServerCallContext *call_context; -} GAFlightServerCallContextPrivate; + std::string current_incoming_header_key; + std::string current_incoming_header_value; +}; enum { PROP_CALL_CONTEXT = 1, @@ -310,6 +312,15 @@ G_DEFINE_TYPE_WITH_PRIVATE(GAFlightServerCallContext, gaflight_server_call_context_get_instance_private( \ GAFLIGHT_SERVER_CALL_CONTEXT(obj))) +static void +gaflight_server_call_context_finalize(GObject *object) +{ + auto priv = GAFLIGHT_SERVER_CALL_CONTEXT_GET_PRIVATE(object); + priv->current_incoming_header_key.~basic_string(); + priv->current_incoming_header_value.~basic_string(); + G_OBJECT_CLASS(gaflight_server_call_context_parent_class)->finalize(object); +} + static void gaflight_server_call_context_set_property(GObject *object, guint prop_id, @@ -333,6 +344,9 @@ gaflight_server_call_context_set_property(GObject *object, static void gaflight_server_call_context_init(GAFlightServerCallContext *object) { + auto priv = GAFLIGHT_SERVER_CALL_CONTEXT_GET_PRIVATE(object); + new(&(priv->current_incoming_header_key)) std::string; + new(&(priv->current_incoming_header_value)) std::string; } static void @@ -340,6 +354,7 @@ gaflight_server_call_context_class_init(GAFlightServerCallContextClass *klass) { auto gobject_class = G_OBJECT_CLASS(klass); + gobject_class->finalize = gaflight_server_call_context_finalize; gobject_class->set_property = gaflight_server_call_context_set_property; GParamSpec *spec; @@ -351,6 +366,33 @@ gaflight_server_call_context_class_init(GAFlightServerCallContextClass *klass) g_object_class_install_property(gobject_class, PROP_CALL_CONTEXT, spec); } +/** + * gaflight_server_call_context_foreach_incoming_header: + * @context: A #GAFlightServerCallContext. + * @func: (scope call): The user's callback function. + * @user_data: (closure): Data for @func. + * + * Iterates over all incoming headers. + * + * Since: 14.0.0 + */ +void +gaflight_server_call_context_foreach_incoming_header( + GAFlightServerCallContext *context, + GAFlightHeaderFunc func, + gpointer user_data) +{ + auto priv = GAFLIGHT_SERVER_CALL_CONTEXT_GET_PRIVATE(context); + auto flight_context = gaflight_server_call_context_get_raw(context); + for (const auto &header : flight_context->incoming_headers()) { + priv->current_incoming_header_key = std::string(header.first); + priv->current_incoming_header_value = std::string(header.second); + func(priv->current_incoming_header_key.c_str(), + priv->current_incoming_header_value.c_str(), + user_data); + } +} + struct GAFlightServerAuthSenderPrivate { arrow::flight::ServerAuthSender *sender; diff --git a/c_glib/arrow-flight-glib/server.h b/c_glib/arrow-flight-glib/server.h index 7fa0dcf878000..d1f5553843bbe 100644 --- a/c_glib/arrow-flight-glib/server.h +++ b/c_glib/arrow-flight-glib/server.h @@ -84,6 +84,13 @@ struct _GAFlightServerCallContextClass GObjectClass parent_class; }; +GARROW_AVAILABLE_IN_14_0 +void +gaflight_server_call_context_foreach_incoming_header( + GAFlightServerCallContext *context, + GAFlightHeaderFunc func, + gpointer user_data); + #define GAFLIGHT_TYPE_SERVER_AUTH_SENDER \ (gaflight_server_auth_sender_get_type()) diff --git a/c_glib/doc/arrow-flight-glib/arrow-flight-glib-docs.xml b/c_glib/doc/arrow-flight-glib/arrow-flight-glib-docs.xml index e078b1c037b5f..e58ff375c5964 100644 --- a/c_glib/doc/arrow-flight-glib/arrow-flight-glib-docs.xml +++ b/c_glib/doc/arrow-flight-glib/arrow-flight-glib-docs.xml @@ -55,6 +55,10 @@ Index of deprecated API + + Index of new symbols in 14.0.0 + + Index of new symbols in 12.0.0 diff --git a/ruby/red-arrow-flight-sql/lib/arrow-flight-sql/client.rb b/ruby/red-arrow-flight-sql/lib/arrow-flight-sql/client.rb index ff3169d5621b2..dc8815fced6e9 100644 --- a/ruby/red-arrow-flight-sql/lib/arrow-flight-sql/client.rb +++ b/ruby/red-arrow-flight-sql/lib/arrow-flight-sql/client.rb @@ -18,13 +18,13 @@ module ArrowFlightSQL class Client alias_method :prepare_raw, :prepare - def prepare(*args) - statement = prepare_raw(*args) + def prepare(query, options=nil) + statement = prepare_raw(query, options) if block_given? begin yield(statement) ensure - statement.close unless statement.closed? + statement.close(options) unless statement.closed? end else statement diff --git a/ruby/red-arrow-flight-sql/test/helper/server.rb b/ruby/red-arrow-flight-sql/test/helper/server.rb index f7c935fab91fa..6426e11f2984d 100644 --- a/ruby/red-arrow-flight-sql/test/helper/server.rb +++ b/ruby/red-arrow-flight-sql/test/helper/server.rb @@ -39,7 +39,7 @@ def virtual_do_do_get_statement(context, command) end def virtual_do_create_prepared_statement(context, request) - unless request.query == "INSERT INTO page_view_table VALUES (?, true)" + unless request.query == "INSERT INTO page_view_table VALUES ($1, true)" raise Arrow::Error::Invalid.new("invalid SQL") end result = ArrowFlightSQL::CreatePreparedStatementResult.new @@ -62,6 +62,11 @@ def virtual_do_close_prepared_statement(context, request) unless request.handle.to_s == "valid-handle" raise Arrow::Error::Invalid.new("invalid handle") end + access_key = context.incoming_headers.assoc("x-access-key") + unless access_key == ["x-access-key", "secret"] + message = "invalid access key: #{access_key.inspect}" + raise Arrow::Error::Invalid.new(message) + end end end end diff --git a/ruby/red-arrow-flight-sql/test/test-client.rb b/ruby/red-arrow-flight-sql/test/test-client.rb index 21554c1bdab84..1fff21da0193c 100644 --- a/ruby/red-arrow-flight-sql/test/test-client.rb +++ b/ruby/red-arrow-flight-sql/test/test-client.rb @@ -41,9 +41,11 @@ def test_execute end def test_prepare - insert_sql = "INSERT INTO page_view_table VALUES (?, true)" + insert_sql = "INSERT INTO page_view_table VALUES ($1, true)" block_called = false - @sql_client.prepare(insert_sql) do |statement| + options = ArrowFlight::CallOptions.new + options.add_header("x-access-key", "secret") + @sql_client.prepare(insert_sql, options) do |statement| block_called = true assert_equal([ Arrow::Schema.new(count: :uint64, private: :boolean), diff --git a/ruby/red-arrow-flight/lib/arrow-flight/loader.rb b/ruby/red-arrow-flight/lib/arrow-flight/loader.rb index 042e15769368e..e9e9e08f32dd9 100644 --- a/ruby/red-arrow-flight/lib/arrow-flight/loader.rb +++ b/ruby/red-arrow-flight/lib/arrow-flight/loader.rb @@ -34,6 +34,7 @@ def require_libraries require "arrow-flight/client-options" require "arrow-flight/location" require "arrow-flight/record-batch-reader" + require "arrow-flight/server-call-context" require "arrow-flight/server-options" require "arrow-flight/ticket" end diff --git a/ruby/red-arrow-flight/lib/arrow-flight/server-call-context.rb b/ruby/red-arrow-flight/lib/arrow-flight/server-call-context.rb new file mode 100644 index 0000000000000..7cbad1fd079df --- /dev/null +++ b/ruby/red-arrow-flight/lib/arrow-flight/server-call-context.rb @@ -0,0 +1,31 @@ +# 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. + +module ArrowFlight + class ServerCallContext + def each_incoming_header + return to_enum(__method__) unless block_given? + foreach_incoming_header do |key, value| + yield(key, value) + end + end + + def incoming_headers + each_incoming_header.to_a + end + end +end