diff --git a/glib/adbc-arrow-glib/adbc-arrow-glib.h b/glib/adbc-arrow-glib/adbc-arrow-glib.h index d9b485f300..00906cbb7b 100644 --- a/glib/adbc-arrow-glib/adbc-arrow-glib.h +++ b/glib/adbc-arrow-glib/adbc-arrow-glib.h @@ -21,4 +21,5 @@ #include +#include #include diff --git a/glib/adbc-arrow-glib/connection.c b/glib/adbc-arrow-glib/connection.c new file mode 100644 index 0000000000..10d593490d --- /dev/null +++ b/glib/adbc-arrow-glib/connection.c @@ -0,0 +1,241 @@ +/* + * 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 + +/** + * SECTION: connection + * @title: GADBCArrowStatement + * @include: adbc-arrow-glib/adbc-arrow-glib.h + * + * #GADBCArrowConnection is a class for Arrow GLib integrated + * connection. + */ + +G_DEFINE_TYPE(GADBCArrowConnection, gadbc_arrow_connection, GADBC_TYPE_CONNECTION) + +static void gadbc_arrow_connection_init(GADBCArrowConnection* connection) {} + +static void gadbc_arrow_connection_class_init(GADBCArrowConnectionClass* klass) {} + +/** + * gadbc_arrow_connection_new: + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: A newly created #GADBCArrowConnection. + * + * Since: 1.0.0 + */ +GADBCArrowConnection* gadbc_arrow_connection_new(GError** error) { + GADBCArrowConnection* connection = g_object_new(GADBC_ARROW_TYPE_CONNECTION, NULL); + if (gadbc_connection_initialize(GADBC_CONNECTION(connection), + "[adbc-arrow][connection][new]", error)) { + return connection; + } else { + g_object_unref(connection); + return NULL; + } +} + +static GArrowRecordBatchReader* gadbc_arrow_import_array_stream( + gpointer c_abi_array_stream, GError** error) { + if (!c_abi_array_stream) { + return NULL; + } + GArrowRecordBatchReader* reader = + garrow_record_batch_reader_import(c_abi_array_stream, error); + g_free(c_abi_array_stream); + return reader; +} + +static GArrowSchema* gadbc_arrow_import_schema(gpointer c_abi_schema, GError** error) { + if (!c_abi_schema) { + return NULL; + } + GArrowSchema* schema = garrow_schema_import(c_abi_schema, error); + g_free(c_abi_schema); + return schema; +} + +/** + * gadbc_arrow_connection_get_info: + * @connection: A #GADBCArrowConnection. + * @info_codes: (nullable) (array length=n_info_codes): A list of + * metadata codes to fetch, or %NULL to fetch all. + * @n_info_codes: The length of the info_codes parameter. Ignored if + * info_codes is %NULL. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_arrow_connection_get_info(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowRecordBatchReader on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowRecordBatchReader* gadbc_arrow_connection_get_info(GADBCArrowConnection* connection, + guint32* info_codes, + gsize n_info_codes, + GError** error) { + gpointer c_abi_array_stream = gadbc_connection_get_info( + GADBC_CONNECTION(connection), info_codes, n_info_codes, error); + return gadbc_arrow_import_array_stream(c_abi_array_stream, error); +} + +/** + * gadbc_arrow_connection_get_objects: + * @connection: A #GADBCArrowConnection. + * @depth: The level of nesting to display. If + * @GADBC_OBJECT_DEPTH_ALL, display all levels. If + * @GADBC_OBJECT_DEPTH_CATALOGS, display only catalogs + * (i.e. `catalog_schemas` will be null). If + * @GADBC_OBJECT_DEPTH_DB_SCHEMAS, display only catalogs and schemas + * (i.e. `db_schema_tables` will be null). if + * @GADBC_OBJECT_DEPTH_TABLES, display only catalogs, schemas and + * tables (i.e. `table_columns` and `table_constraints` will be + * null). + * @catalog: (nullable): Only show tables in the given catalog. If + * %NULL, do not filter by catalog. If an empty string, only show + * tables without a catalog. May be a search pattern (see section + * documentation). + * @db_schema: (nullable): Only show tables in the given database + * schema. If %NULL, do not filter by database schema. If an empty + * string, only show tables without a database schema. May be a + * search pattern (see section documentation). + * @table_name: (nullable): Only show tables with the given name. If + * %NULL, do not filter by name. May be a search pattern (see + * section documentation). + * @table_types: (nullable) (array zero-terminated=1): Only show + * tables matching one of the given table types. If %NULL, show + * tables of any type. Valid table types can be fetched from + * gadbc_connection_get_table_types(). Terminate the list with a + * %NULL entry. + * @column_name: (nullable): Only show columns with the given name. If + * %NULL, do not filter by name. May be a search pattern (see section + * documentation). + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_connection_get_objects(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowRecordBatchReader on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowRecordBatchReader* gadbc_arrow_connection_get_objects( + GADBCArrowConnection* connection, GADBCObjectDepth depth, const gchar* catalog, + const gchar* db_schema, const gchar* table_name, const gchar** table_types, + const gchar* column_name, GError** error) { + gpointer c_abi_array_stream = gadbc_connection_get_objects( + GADBC_CONNECTION(connection), depth, catalog, db_schema, table_name, table_types, + column_name, error); + return gadbc_arrow_import_array_stream(c_abi_array_stream, error); +} + +/** + * gadbc_arrow_connection_get_table_schema: + * @connection: A #GADBCArrowConnection. + * @catalog: (nullable): A catalog or %NULL if not applicable. + * @db_schema: (nullable): A database schema or %NULL if not applicable. + * @table_name: A table name. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_connection_get_table_schema(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowSchema on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowSchema* gadbc_arrow_connection_get_table_schema(GADBCArrowConnection* connection, + const gchar* catalog, + const gchar* db_schema, + const gchar* table_name, + GError** error) { + gpointer c_abi_schema = gadbc_connection_get_table_schema( + GADBC_CONNECTION(connection), catalog, db_schema, table_name, error); + return gadbc_arrow_import_schema(c_abi_schema, error); +} + +/** + * gadbc_arrow_connection_get_table_types: + * @connection: A #GADBCArrowConnection. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_connection_get_table_types(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowRecordBatchReader on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowRecordBatchReader* gadbc_arrow_connection_get_table_types( + GADBCArrowConnection* connection, GError** error) { + gpointer c_abi_array_stream = + gadbc_connection_get_table_types(GADBC_CONNECTION(connection), error); + return gadbc_arrow_import_array_stream(c_abi_array_stream, error); +} + +/** + * gadbc_arrow_connection_get_statistics: + * @connection: A #GADBCArrowConnection. + * @catalog: (nullable): A catalog or %NULL if not applicable. + * @db_schema: (nullable): A database schema or %NULL if not applicable. + * @table_name: (nullable): A table name. + * @approximate: Whether approximate values are allowed or not. If + * this is %TRUE, best-effort, approximate or cached values may be + * returned. Otherwise, exact values are requested. Note that the + * database may return approximate values regardless as indicated + * in the result. Request exact values may be expensive or + * unsupported. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_arrow_connection_get_statistics(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowRecordBatchReader on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowRecordBatchReader* gadbc_arrow_connection_get_statistics( + GADBCArrowConnection* connection, const gchar* catalog, const gchar* db_schema, + const gchar* table_name, gboolean approximate, GError** error) { + gpointer c_abi_array_stream = gadbc_connection_get_statistics( + GADBC_CONNECTION(connection), catalog, db_schema, table_name, approximate, error); + return gadbc_arrow_import_array_stream(c_abi_array_stream, error); +} + +/** + * gadbc_arrow_connection_get_statistic_names: + * @connection: A #GADBCArrowConnection. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is a wrapper of gadbc_connection_get_statistic_names(). + * + * Returns: (transfer full) (nullable): The result set as + * #GArrowRecordBatchReader on success, %NULL on error. + * + * Since: 1.0.0 + */ +GArrowRecordBatchReader* gadbc_arrow_connection_get_statistic_names( + GADBCArrowConnection* connection, GError** error) { + gpointer c_abi_array_stream = + gadbc_connection_get_statistic_names(GADBC_CONNECTION(connection), error); + return gadbc_arrow_import_array_stream(c_abi_array_stream, error); +} diff --git a/glib/adbc-arrow-glib/connection.h b/glib/adbc-arrow-glib/connection.h new file mode 100644 index 0000000000..0c3856fa4a --- /dev/null +++ b/glib/adbc-arrow-glib/connection.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 +#include + +#include + +G_BEGIN_DECLS + +#define GADBC_ARROW_TYPE_CONNECTION (gadbc_arrow_connection_get_type()) +G_DECLARE_DERIVABLE_TYPE(GADBCArrowConnection, gadbc_arrow_connection, GADBCArrow, + CONNECTION, GADBCConnection) +struct _GADBCArrowConnectionClass { + GADBCConnectionClass parent_class; +}; + +GADBC_ARROW_AVAILABLE_IN_1_0 +GADBCArrowConnection* gadbc_arrow_connection_new(GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowRecordBatchReader* gadbc_arrow_connection_get_info(GADBCArrowConnection* connection, + guint32* info_codes, + gsize n_info_codes, + GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowRecordBatchReader* gadbc_arrow_connection_get_objects( + GADBCArrowConnection* connection, GADBCObjectDepth depth, const gchar* catalog, + const gchar* db_schema, const gchar* table_name, const gchar** table_types, + const gchar* column_name, GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowSchema* gadbc_arrow_connection_get_table_schema(GADBCArrowConnection* connection, + const gchar* catalog, + const gchar* db_schema, + const gchar* table_name, + GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowRecordBatchReader* gadbc_arrow_connection_get_table_types( + GADBCArrowConnection* connection, GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowRecordBatchReader* gadbc_arrow_connection_get_statistics( + GADBCArrowConnection* connection, const gchar* catalog, const gchar* db_schema, + const gchar* table_name, gboolean approximate, GError** error); +GADBC_ARROW_AVAILABLE_IN_1_0 +GArrowRecordBatchReader* gadbc_arrow_connection_get_statistic_names( + GADBCArrowConnection* connection, GError** error); + +G_END_DECLS diff --git a/glib/adbc-arrow-glib/meson.build b/glib/adbc-arrow-glib/meson.build index 65103693d5..5780403905 100644 --- a/glib/adbc-arrow-glib/meson.build +++ b/glib/adbc-arrow-glib/meson.build @@ -18,18 +18,15 @@ # under the License. sources = files( + 'connection.c', 'statement.c', ) definition_headers = files( + 'connection.h', 'statement.h', ) -headers = definition_headers -headers += files( - 'adbc-arrow-glib.h', -) - version_h_conf = configuration_data() version_h_conf.set('GADBC_ARROW_VERSION', meson.project_version()) version_h_conf.set('GADBC_ARROW_VERSION_MAJOR', version_major) @@ -40,6 +37,11 @@ version_h = configure_file(input: 'version.h.in', configuration: version_h_conf) headers += version_h +headers = definition_headers +headers += files( + 'adbc-arrow-glib.h', +) + enums = gnome.mkenums_simple('enum-types', identifier_prefix: 'GADBCArrow', sources: definition_headers, diff --git a/glib/adbc-arrow-glib/statement.c b/glib/adbc-arrow-glib/statement.c index 8f9352f87a..ab5780b776 100644 --- a/glib/adbc-arrow-glib/statement.c +++ b/glib/adbc-arrow-glib/statement.c @@ -25,7 +25,7 @@ * @include: adbc-arrow-glib/adbc-arrow-glib.h * * #GADBCArrowStatement is a class for Arrow GLib integrated - statement. + * statement. */ G_DEFINE_TYPE(GADBCArrowStatement, gadbc_arrow_statement, GADBC_TYPE_STATEMENT) diff --git a/glib/adbc-arrow-glib/version.h.in b/glib/adbc-arrow-glib/version.h.in index 6fd0068839..266ff5064b 100644 --- a/glib/adbc-arrow-glib/version.h.in +++ b/glib/adbc-arrow-glib/version.h.in @@ -100,6 +100,15 @@ # define GADBC_ARROW_UNAVAILABLE(major, minor) G_UNAVAILABLE(major, minor) #endif +/** + * GADBC_ARROW_VERSION_1_0: + * + * You can use this macro value for compile time API version check. + * + * Since: 1.0.0 + */ +#define GADBC_ARROW_VERSION_1_0 G_ENCODE_VERSION(1, 0) + /** * GADBC_ARROW_VERSION_0_10: * @@ -156,6 +165,20 @@ #define GADBC_ARROW_AVAILABLE_IN_ALL +#if GADBC_ARROW_VERSION_MIN_REQUIRED >= GADBC_ARROW_VERSION_1_0 +# define GADBC_ARROW_DEPRECATED_IN_1_0 GADBC_ARROW_DEPRECATED +# define GADBC_ARROW_DEPRECATED_IN_1_0_FOR(function) GADBC_ARROW_DEPRECATED_FOR(function) +#else +# define GADBC_ARROW_DEPRECATED_IN_1_0 +# define GADBC_ARROW_DEPRECATED_IN_1_0_FOR(function) +#endif + +#if GADBC_ARROW_VERSION_MAX_ALLOWED < GADBC_ARROW_VERSION_1_0 +# define GADBC_ARROW_AVAILABLE_IN_1_0 GADBC_ARROW_UNAVAILABLE(1, 0) +#else +# define GADBC_ARROW_AVAILABLE_IN_1_0 +#endif + #if GADBC_ARROW_VERSION_MIN_REQUIRED >= GADBC_ARROW_VERSION_0_10 # define GADBC_ARROW_DEPRECATED_IN_0_10 GADBC_ARROW_DEPRECATED # define GADBC_ARROW_DEPRECATED_IN_0_10_FOR(function) GADBC_ARROW_DEPRECATED_FOR(function) diff --git a/glib/adbc-glib/connection.c b/glib/adbc-glib/connection.c index 3bfb2b48b4..2c183b5645 100644 --- a/glib/adbc-glib/connection.c +++ b/glib/adbc-glib/connection.c @@ -126,6 +126,30 @@ static void gadbc_connection_class_init(GADBCConnectionClass* klass) { gobject_class->dispose = gadbc_connection_dispose; } +/** + * gadbc_connection_initialize: (skip) + * @connection: A #GADBCConnection. + * @context: A context for error message. + * @error: (nullable): Return location for a #GError or %NULL. + * + * This is only for implementing subclass of #GADBCConnection. In + * general, users should use gadbc_connection_new(). + * + * Initializes an empty #GADBCConnection. + * + * Returns: %TRUE if initialization is done successfully, %FALSE otherwise. + * + * Since: 1.0.0 + */ +gboolean gadbc_connection_initialize(GADBCConnection* connection, const gchar* context, + GError** error) { + GADBCConnectionPrivate* priv = gadbc_connection_get_instance_private(connection); + struct AdbcError adbc_error = {}; + AdbcStatusCode status_code = AdbcConnectionNew(&(priv->adbc_connection), &adbc_error); + priv->initialized = gadbc_error_check(error, status_code, &adbc_error, context); + return priv->initialized; +} + /** * gadbc_connection_new: * @error: (nullable): Return location for a #GError or %NULL. @@ -136,16 +160,12 @@ static void gadbc_connection_class_init(GADBCConnectionClass* klass) { */ GADBCConnection* gadbc_connection_new(GError** error) { GADBCConnection* connection = g_object_new(GADBC_TYPE_CONNECTION, NULL); - GADBCConnectionPrivate* priv = gadbc_connection_get_instance_private(connection); - struct AdbcError adbc_error = {}; - AdbcStatusCode status_code = AdbcConnectionNew(&(priv->adbc_connection), &adbc_error); - priv->initialized = - gadbc_error_check(error, status_code, &adbc_error, "[adbc][connection][new]"); - if (!priv->initialized) { + if (gadbc_connection_initialize(connection, "[adbc][connection][new]", error)) { + return connection; + } else { g_object_unref(connection); return NULL; } - return connection; } /** @@ -324,9 +344,10 @@ gboolean gadbc_connection_init(GADBCConnection* connection, GADBCDatabase* datab * for ADBC usage. Drivers/vendors will ignore requests for * unrecognized codes (the row will be omitted from the result). * - * Returns: The result set as `struct ArrowArrayStream *`. It should - * be freed with the `ArrowArrayStream::release` callback then - * g_free() when no longer needed. + * Returns: (nullable): The result set as `struct ArrowArrayStream *` + * on success, %NULL on error. It should be freed with the + * `ArrowArrayStream::release` callback then g_free() when no longer + * needed. * * Since: 0.4.0 */ diff --git a/glib/adbc-glib/connection.h b/glib/adbc-glib/connection.h index 2088986bf1..f5ca26671c 100644 --- a/glib/adbc-glib/connection.h +++ b/glib/adbc-glib/connection.h @@ -213,6 +213,9 @@ struct _GADBCConnectionClass { GObjectClass parent_class; }; +GADBC_AVAILABLE_IN_1_0 +gboolean gadbc_connection_initialize(GADBCConnection* connection, const gchar* context, + GError** error); GADBC_AVAILABLE_IN_0_1 GADBCConnection* gadbc_connection_new(GError** error); GADBC_AVAILABLE_IN_0_1 diff --git a/glib/adbc-glib/statement.c b/glib/adbc-glib/statement.c index cb17eb3c12..7a19789762 100644 --- a/glib/adbc-glib/statement.c +++ b/glib/adbc-glib/statement.c @@ -82,7 +82,7 @@ static void gadbc_statement_class_init(GADBCStatementClass* klass) { * Since: 0.10.0 */ gboolean gadbc_statement_initialize(GADBCStatement* statement, - GADBCConnection* connection, const char* context, + GADBCConnection* connection, const gchar* context, GError** error) { struct AdbcConnection* adbc_connection = gadbc_connection_get_raw(connection, context, error); diff --git a/glib/test/helper.rb b/glib/test/helper.rb index a3a19e6c5e..7e99ac8108 100644 --- a/glib/test/helper.rb +++ b/glib/test/helper.rb @@ -62,4 +62,48 @@ def execute_sql(connection, sql, need_result: true, &block) statement.release end end + + def normalize_vendor_version(version) + return nil if version.nil? + version.gsub(/\A\d+(?:\.\d+)*\z/, "X.Y.Z") + end + + def normalize_arrow_version(version) + return nil if version.nil? + version.gsub(/\A\d+\.\d+\.\d+(?:-SNAPSHOT)?\z/, "X.Y.Z") + end + + def normalize_info(info) + info.collect do |code, value| + value = value.values[0] if value.is_a?(Hash) + case code + when ADBC::Info::VENDOR_VERSION + value = normalize_vendor_version(value) + when ADBC::Info::DRIVER_ARROW_VERSION + value = normalize_arrow_version(value) + end + [code, value] + end + end + + def normalize_statistics(statistics) + statistics.each do |name, db_schemas| + db_schemas.each do |db_schema| + db_schema["db_schema_statistics"].each do |stat| + key = stat["statistic_key"] + stat["statistic_key"] = ADBC::StatisticKey.new(key) + value = stat["statistic_value"] + stat["statistic_value"] = value.round(1) if value.is_a?(Float) + end + db_schema["db_schema_statistics"].sort_by! do |stat| + [ + stat["table_name"], + stat["column_name"] || "", + stat["statistic_key"].to_i, + ] + end + end + end + statistics + end end diff --git a/glib/test/test-arrow-connection.rb b/glib/test/test-arrow-connection.rb new file mode 100644 index 0000000000..5827417a3a --- /dev/null +++ b/glib/test/test-arrow-connection.rb @@ -0,0 +1,187 @@ +# 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. + +class ArrowConnectionTest < Test::Unit::TestCase + include Helper + include Helper::Sandbox + + setup def setup_database + omit("adbc-arrow-glib isn't built") unless defined?(ADBCArrow) + + @database = ADBC::Database.new + @database.set_option("driver", "adbc_driver_postgresql") + @database.set_option("uri", adbc_uri) + @database.init + open_connection do |connection| + @connection = connection + yield + end + end + + def open_connection + connection = ADBCArrow::Connection.new + begin + connection.init(@database) + yield(connection) + ensure + connection.release + end + end + + def test_info + reader = @connection.get_info + table = reader.read_all + assert_equal([ + [ADBC::Info::VENDOR_NAME, "PostgreSQL"], + [ADBC::Info::VENDOR_VERSION, "X.Y.Z"], + [ADBC::Info::DRIVER_NAME, "ADBC PostgreSQL Driver"], + [ADBC::Info::DRIVER_VERSION, "(unknown)"], + [ADBC::Info::DRIVER_ARROW_VERSION, "X.Y.Z"], + [ADBC::Info::DRIVER_ADBC_VERSION, ADBC::VERSION_1_1_0], + ], + normalize_info(table.raw_records)) + end + + def test_objects + reader = @connection.get_objects(:catalogs) + assert_equal([ + ["postgres", nil], + [@test_db_name, nil], + ["template1", nil], + ["template0", nil], + ], + reader.read_all.raw_records) + end + + def test_table_schema + execute_sql(@connection, + "CREATE TABLE data (number int, string text)", + need_result: false) + execute_sql(@connection, + "INSERT INTO data VALUES (1, 'hello')", + need_result: false) + assert_equal(Arrow::Schema.new(number: :int32, + string: :string), + @connection.get_table_schema(nil, nil, "data")) + end + + def test_table_types + reader = @connection.table_types + table = reader.read_all + fields = [ + Arrow::Field.new("table_type", :string, false), + ] + schema = Arrow::Schema.new(fields) + table_types = Arrow::StringArray.new([ + "partitioned_table", + "foreign_table", + "toast_table", + "materialized_view", + "view", + "table", + ]) + assert_equal(Arrow::Table.new(schema, [table_types]), + table) + end + + def test_statistics + run_sql("CREATE TABLE public.data1 (number int)") + run_sql("INSERT INTO public.data1 VALUES (1), (NULL), (2)") + run_sql("CREATE TABLE public.data2 (name text)") + run_sql("INSERT INTO public.data2 VALUES ('hello'), (NULL)") + run_sql("ANALYZE") + reader = @connection.get_statistics(nil, "public", nil, true) + table = reader.read_all + assert_equal( + [ + [ + @test_db_name, + [ + { + "db_schema_name" => "public", + "db_schema_statistics" => [ + { + "table_name" => "data1", + "column_name" => nil, + "statistic_key" => ADBC::StatisticKey::ROW_COUNT, + "statistic_value" => 3.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data1", + "column_name" => "number", + "statistic_key" => ADBC::StatisticKey::AVERAGE_BYTE_WIDTH, + "statistic_value" => 4.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data1", + "column_name" => "number", + "statistic_key" => ADBC::StatisticKey::DISTINCT_COUNT, + "statistic_value" => 2.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data1", + "column_name" => "number", + "statistic_key" => ADBC::StatisticKey::NULL_COUNT, + "statistic_value" => 1.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data2", + "column_name" => nil, + "statistic_key" => ADBC::StatisticKey::ROW_COUNT, + "statistic_value" => 2.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data2", + "column_name" => "name", + "statistic_key" => ADBC::StatisticKey::AVERAGE_BYTE_WIDTH, + "statistic_value" => 6.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data2", + "column_name" => "name", + "statistic_key" => ADBC::StatisticKey::DISTINCT_COUNT, + "statistic_value" => 1.0, + "statistic_is_approximate" => true, + }, + { + "table_name" => "data2", + "column_name" => "name", + "statistic_key" => ADBC::StatisticKey::NULL_COUNT, + "statistic_value" => 1.0, + "statistic_is_approximate" => true, + }, + ], + }, + ], + ], + ], + normalize_statistics(table.raw_records) + ) + end + + def test_statistic_names + reader = @connection.statistic_names + assert_equal([], + reader.read_all.raw_records) + end +end diff --git a/glib/test/test-connection.rb b/glib/test/test-connection.rb index 80b7bf1a15..b6d83ac8cd 100644 --- a/glib/test/test-connection.rb +++ b/glib/test/test-connection.rb @@ -40,29 +40,6 @@ def open_connection end end - def normalize_vendor_version(version) - return nil if version.nil? - version.gsub(/\A\d+(?:\.\d+)*\z/, "X.Y.Z") - end - - def normalize_arrow_version(version) - return nil if version.nil? - version.gsub(/\A\d+\.\d+\.\d+(?:-SNAPSHOT)?\z/, "X.Y.Z") - end - - def normalize_info(info) - info.collect do |code, value| - value = value.values[0] if value.is_a?(Hash) - case code - when ADBC::Info::VENDOR_VERSION - value = normalize_vendor_version(value) - when ADBC::Info::DRIVER_ARROW_VERSION - value = normalize_arrow_version(value) - end - [code, value] - end - end - sub_test_case("#get_info") do def test_all c_abi_array_stream = @connection.get_info @@ -554,27 +531,6 @@ def test_isolation_level end sub_test_case("#get_statistics") do - def normalize_statistics(statistics) - statistics.each do |name, db_schemas| - db_schemas.each do |db_schema| - db_schema["db_schema_statistics"].each do |stat| - key = stat["statistic_key"] - stat["statistic_key"] = ADBC::StatisticKey.new(key) - value = stat["statistic_value"] - stat["statistic_value"] = value.round(1) if value.is_a?(Float) - end - db_schema["db_schema_statistics"].sort_by! do |stat| - [ - stat["table_name"], - stat["column_name"] || "", - stat["statistic_key"].to_i, - ] - end - end - end - statistics - end - def test_schema run_sql("CREATE TABLE public.data1 (number int)") run_sql("INSERT INTO public.data1 VALUES (1), (NULL), (2)") diff --git a/ruby/lib/adbc.rb b/ruby/lib/adbc.rb index f2482bfb5a..62455954fd 100644 --- a/ruby/lib/adbc.rb +++ b/ruby/lib/adbc.rb @@ -32,6 +32,10 @@ class Error < StandardError rescue GObjectIntrospection::RepositoryError else module ADBC + RawConnection = Connection + remove_const(:Connection) + Connection = ADBCArrow::Connection + RawStatement = Statement remove_const(:Statement) Statement = ADBCArrow::Statement diff --git a/ruby/lib/adbc/arrow-connection.rb b/ruby/lib/adbc/arrow-connection.rb new file mode 100644 index 0000000000..e84c533b0e --- /dev/null +++ b/ruby/lib/adbc/arrow-connection.rb @@ -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. + +module ADBCArrow + class Connection + include ADBC::ConnectionOperations + + def open_statement(&block) + Statement.open(self, &block) + end + + alias_method :get_info_raw, :get_info + def get_info(codes) + reader = get_info_raw(codes) + begin + yield(reader.read_all) + ensure + reader.unref + end + end + end +end diff --git a/ruby/lib/adbc/connection-operations.rb b/ruby/lib/adbc/connection-operations.rb new file mode 100644 index 0000000000..535ab7cfb9 --- /dev/null +++ b/ruby/lib/adbc/connection-operations.rb @@ -0,0 +1,56 @@ +# 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 ADBC + module ConnectionOperations + def query(sql, &block) + open_statement do |statement| + statement.query(sql, &block) + end + end + + def ingest(table_name, values, mode: :create) + open_statment do |statement| + statement.ingest(table_name, values, mode: mode) + end + end + + def info(codes=nil) + unless codes.nil? + codes = codes.collect do |code| + ADBC::Info.try_convert(code) + end + end + get_info(codes) do |table| + values = {} + table.raw_records.each do |code, value| + value = value.values[0] if value.is_a?(Hash) + code = ADBC::Info.try_convert(code) + values[code.nick.gsub("-", "_").to_sym] = value + end + values + end + end + + ADBC::Info.values.each do |value| + name = value.nick.gsub("-", "_").to_sym + define_method(name) do + info([name])[name] + end + end + end +end diff --git a/ruby/lib/adbc/connection.rb b/ruby/lib/adbc/connection.rb index 186c5da741..5ba28a803f 100644 --- a/ruby/lib/adbc/connection.rb +++ b/ruby/lib/adbc/connection.rb @@ -15,42 +15,23 @@ # specific language governing permissions and limitations # under the License. +require_relative "connection-operations" + module ADBC class Connection + include ConnectionOperations + def open_statement(&block) Statement.open(self, &block) end - def query(sql, &block) - open_statement do |statement| - statement.query(sql, &block) - end - end - - def ingest(table_name, values, mode: :create) - open_statment do |statement| - statement.ingest(table_name, values, mode: mode) - end - end - - def info(codes=nil) - unless codes.nil? - codes = codes.collect do |code| - ADBC::Info.try_convert(code) - end - end - c_abi_array_stream = get_info(codes) + alias_method :get_info_raw, :get_info + def get_info(codes) + c_abi_array_stream = get_info_raw(codes) begin reader = Arrow::RecordBatchReader.import(c_abi_array_stream) begin - table = reader.read_all - values = {} - table.raw_records.each do |code, value| - value = value.values[0] if value.is_a?(Hash) - code = ADBC::Info.try_convert(code) - values[code.nick.gsub("-", "_").to_sym] = value - end - values + yield(reader.read_all) ensure reader.unref end @@ -58,12 +39,5 @@ def info(codes=nil) GLib.free(c_abi_array_stream) end end - - ADBC::Info.values.each do |value| - name = value.nick.gsub("-", "_").to_sym - define_method(name) do - info([name])[name] - end - end end end diff --git a/ruby/lib/adbc/loader.rb b/ruby/lib/adbc/loader.rb index 0e6468b2d1..1abbf2f9c6 100644 --- a/ruby/lib/adbc/loader.rb +++ b/ruby/lib/adbc/loader.rb @@ -58,6 +58,7 @@ def post_load(repository, namespace) end def require_libraries + require_relative "arrow-connection" require_relative "arrow-statement" end end