This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new c0bf4bd51 feat(glib): Add garrow_connection_get_statistics() (#1744)
c0bf4bd51 is described below
commit c0bf4bd51c95d6985ba4491150539cb7e3a4e42d
Author: Sutou Kouhei <[email protected]>
AuthorDate: Tue Apr 23 08:54:29 2024 +0900
feat(glib): Add garrow_connection_get_statistics() (#1744)
Fixes #1743.
We use PostgreSQL instead of SQLite for testing. Because the SQLite
driver doesn't support GetStatistics yet.
---
ci/conda_env_glib.txt | 1 +
glib/adbc-glib/connection.c | 116 +++++++
glib/adbc-glib/connection.h | 73 ++++
glib/adbc-glib/meson.build | 20 +-
glib/adbc-glib/version.h.in | 23 ++
glib/test/helper.rb | 24 +-
glib/test/helper/sandbox.rb | 369 ++++++++++++++++++++
glib/test/run.rb | 3 +
glib/test/test-connection.rb | 385 ++++++++++++++++-----
.../test/test-statistic-key.rb | 13 +-
10 files changed, 913 insertions(+), 114 deletions(-)
diff --git a/ci/conda_env_glib.txt b/ci/conda_env_glib.txt
index cd937ee22..883ff7f92 100644
--- a/ci/conda_env_glib.txt
+++ b/ci/conda_env_glib.txt
@@ -19,4 +19,5 @@ arrow-c-glib
glib
gobject-introspection
meson
+postgresql
ruby
diff --git a/glib/adbc-glib/connection.c b/glib/adbc-glib/connection.c
index bcb541c5d..14ec48f06 100644
--- a/glib/adbc-glib/connection.c
+++ b/glib/adbc-glib/connection.c
@@ -35,6 +35,35 @@
#define BOOLEAN_TO_OPTION_VALUE(boolean) \
((boolean) ? ADBC_OPTION_VALUE_ENABLED : ADBC_OPTION_VALUE_DISABLED)
+/**
+ * gadbc_statistic_key_to_string:
+ * @key: A #GADBCStatisticsKey.
+ *
+ * Returns: The name of @key.
+ *
+ * Since: 1.0.0
+ */
+const gchar* gadbc_statistic_key_to_string(GADBCStatisticKey key) {
+ switch (key) {
+ case GADBC_STATISTIC_KEY_AVERAGE_BYTE_WIDTH:
+ return ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_NAME;
+ case GADBC_STATISTIC_KEY_DISTINCT_COUNT:
+ return ADBC_STATISTIC_DISTINCT_COUNT_NAME;
+ case GADBC_STATISTIC_KEY_MAX_BYTE_WIDTH:
+ return ADBC_STATISTIC_MAX_BYTE_WIDTH_NAME;
+ case GADBC_STATISTIC_KEY_MAX_VALUE:
+ return ADBC_STATISTIC_MAX_VALUE_NAME;
+ case GADBC_STATISTIC_KEY_MIN_VALUE:
+ return ADBC_STATISTIC_MIN_VALUE_NAME;
+ case GADBC_STATISTIC_KEY_NULL_COUNT:
+ return ADBC_STATISTIC_NULL_COUNT_NAME;
+ case GADBC_STATISTIC_KEY_ROW_COUNT:
+ return ADBC_STATISTIC_ROW_COUNT_NAME;
+ default:
+ return "adbc.statistic.invalid";
+ }
+}
+
/**
* gadbc_isolation_level_to_string:
* @level: A #GADBCIsolationLevel.
@@ -538,6 +567,93 @@ gpointer gadbc_connection_get_table_types(GADBCConnection*
connection, GError**
}
}
+/**
+ * gadbc_connection_get_statistics:
+ * @connection: A #GADBCConnection.
+ * @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.
+ *
+ * The result is an Arrow dataset with the following schema:
+ *
+ * | Field Name | Field Type |
+ * |--------------------------|----------------------------------|
+ * | catalog_name | utf8 |
+ * | catalog_db_schemas | list<DB_SCHEMA_SCHEMA> not null |
+ *
+ * DB_SCHEMA_SCHEMA is a Struct with fields:
+ *
+ * | Field Name | Field Type |
+ * |--------------------------|----------------------------------|
+ * | db_schema_name | utf8 |
+ * | db_schema_statistics | list<STATISTICS_SCHEMA> not null |
+ *
+ * STATISTICS_SCHEMA is a Struct with fields:
+ *
+ * | Field Name | Field Type | Comments |
+ * |--------------------------|----------------------------------| -------- |
+ * | table_name | utf8 not null | |
+ * | column_name | utf8 | (1) |
+ * | statistic_key | int16 not null | (2) |
+ * | statistic_value | VALUE_SCHEMA not null | |
+ * | statistic_is_approximate | bool not null | (3) |
+ *
+ * 1. If null, then the statistic applies to the entire table.
+ * 2. A dictionary-encoded statistic name (although we do not use the Arrow
+ * dictionary type). Values in [0, 1024) are reserved for ADBC. Other
+ * values are for implementation-specific statistics. For the definitions
+ * of predefined statistic types, see %GADBCStatistics. To get
+ * driver-specific statistic names, use
+ * gadbc_connection_get_statistic_names().
+ * 3. If true, then the value is approximate or best-effort.
+ *
+ * VALUE_SCHEMA is a dense union with members:
+ *
+ * | Field Name | Field Type |
+ * |--------------------------|----------------------------------|
+ * | int64 | int64 |
+ * | uint64 | uint64 |
+ * | float64 | float64 |
+ * | binary | binary |
+ *
+ * Returns: The result set as `struct ArrowArrayStream *`. It should
+ * be freed with the `ArrowArrayStream:release` callback then
+ * g_free() when no longer needed.
+ *
+ * This GADBCConnection must outlive the returned stream.
+ *
+ * Since: 1.0.0
+ */
+gpointer gadbc_connection_get_statistics(GADBCConnection* connection,
+ const gchar* catalog, const gchar*
db_schema,
+ const gchar* table_name, gboolean
approximate,
+ GError** error) {
+ const gchar* context = "[adbc][connection][get-statistics]";
+ struct AdbcConnection* adbc_connection =
+ gadbc_connection_get_raw(connection, context, error);
+ if (!adbc_connection) {
+ return NULL;
+ }
+ struct ArrowArrayStream* array_stream = g_new0(struct ArrowArrayStream, 1);
+ struct AdbcError adbc_error = {};
+ AdbcStatusCode status_code =
+ AdbcConnectionGetStatistics(adbc_connection, catalog, db_schema,
table_name,
+ approximate, array_stream, &adbc_error);
+ if (gadbc_error_check(error, status_code, &adbc_error, context)) {
+ return array_stream;
+ } else {
+ g_free(array_stream);
+ return NULL;
+ }
+}
+
/**
* gadbc_connection_commit:
* @connection: A #GADBCConnection.
diff --git a/glib/adbc-glib/connection.h b/glib/adbc-glib/connection.h
index 30df5710d..210af053f 100644
--- a/glib/adbc-glib/connection.h
+++ b/glib/adbc-glib/connection.h
@@ -23,6 +23,24 @@
G_BEGIN_DECLS
+/**
+ * GADBC_VERSION_1_0_0:
+ *
+ * ADBC revision 1.0.0.
+ *
+ * Since: 1.0.0
+ */
+#define GADBC_VERSION_1_0_0 1000000
+
+/**
+ * GADBC_VERSION_1_1_0:
+ *
+ * ADBC revision 1.1.0.
+ *
+ * Since: 1.0.0
+ */
+#define GADBC_VERSION_1_1_0 1001000
+
/**
* GADBCInfo:
* @GADBC_INFO_VENDOR_NAME: The database vendor/product name (e.g. the
@@ -35,6 +53,8 @@ G_BEGIN_DECLS
* @GADBC_INFO_DRIVER_VERSION: The driver version (type: utf8).
* @GADBC_INFO_DRIVER_ARROW_VERSION: The driver Arrow library version
* (type: utf8).
+ * @GADBC_INFO_DRIVER_ADBC_VERSION: The driver ADBC version
+ * (type: int64).
*
* The information code that is used by gadbc_connection_get_info().
*
@@ -49,6 +69,7 @@ typedef enum {
GADBC_INFO_DRIVER_NAME = 100,
GADBC_INFO_DRIVER_VERSION = 101,
GADBC_INFO_DRIVER_ARROW_VERSION = 102,
+ GADBC_INFO_DRIVER_ADBC_VERSION = 103,
} GADBCInfo;
/**
@@ -76,6 +97,53 @@ typedef enum {
GADBC_OBJECT_DEPTH_TABLES = 3,
} GADBCObjectDepth;
+/**
+ * GADBCStatisticKey:
+ * @GADBC_STATISTICS_KEY_AVERAGE_BYTE_WIDTH: The average byte
+ * width statistic. The average size in bytes of a row in the
+ * column. Value type is float64.
+ * For example, this is roughly the average length of a string for a
+ * string column.Return metadata on catalogs, schemas, tables, and
+ * columns.
+ * @GADBC_STATISTICS_KEY_DISTINCT_COUNT: The distinct value count
+ * (NDV) statistic. The number of distinct values in the column.
+ * Value type is int64 (when not approximate) or float64 (when
+ * approximate).
+ * @GADBC_STATISTICS_KEY_MAX_BYTE_WIDTH: The max byte width statistic.
+ * The maximum size in bytes of a row in the column. Value type is
+ * int64 (when not approximate) or float64 (when approximate).
+ * For example, this is the maximum length of a string for a string
+ * column.
+ * @GADBC_STATISTICS_KEY_MAX_VALUE: The max value statistic. Value
+ * type is column-dependent.
+ * @GADBC_STATISTICS_KEY_MIN_VALUE: The min value statistic. Value
+ * type is column-dependent.
+ * @GADBC_STATISTICS_KEY_NULL_COUNT: The null count statistic. The
+ * number of values that are null in the column. Value type is
+ * int64 (when not approximate) or float64 (when approximate).
+ * @GADBC_STATISTICS_KEY_ROW_COUNT: The row count statistic. The
+ * number of rows in the column or table. Value type is int64 (when
+ * not approximate) or float64 (when approximate).
+ *
+ * Standard statistic names for gadbc_connection_get_statistics().
+ *
+ * They are corresponding to `ADBC_STATISTIC_*_KEY` values in `adbc.h`.
+ *
+ * Since: 1.0.0
+ */
+typedef enum {
+ GADBC_STATISTIC_KEY_AVERAGE_BYTE_WIDTH = 0,
+ GADBC_STATISTIC_KEY_DISTINCT_COUNT = 1,
+ GADBC_STATISTIC_KEY_MAX_BYTE_WIDTH = 2,
+ GADBC_STATISTIC_KEY_MAX_VALUE = 3,
+ GADBC_STATISTIC_KEY_MIN_VALUE = 4,
+ GADBC_STATISTIC_KEY_NULL_COUNT = 5,
+ GADBC_STATISTIC_KEY_ROW_COUNT = 6,
+} GADBCStatisticKey;
+
+GADBC_AVAILABLE_IN_1_0
+const gchar* gadbc_statistic_key_to_string(GADBCStatisticKey key);
+
/**
* GADBCIsolationLevel:
* @GADBC_ISOLATION_LEVEL_DEFAULT: Use database or driver default
@@ -179,6 +247,11 @@ gpointer
gadbc_connection_get_table_schema(GADBCConnection* connection,
const gchar* table_name, GError**
error);
GADBC_AVAILABLE_IN_0_4
gpointer gadbc_connection_get_table_types(GADBCConnection* connection,
GError** error);
+GADBC_AVAILABLE_IN_1_0
+gpointer gadbc_connection_get_statistics(GADBCConnection* connection,
+ const gchar* catalog, const gchar*
db_schema,
+ const gchar* table_name, gboolean
approximate,
+ GError** error);
GADBC_AVAILABLE_IN_0_4
gboolean gadbc_connection_commit(GADBCConnection* connection, GError** error);
GADBC_AVAILABLE_IN_0_4
diff --git a/glib/adbc-glib/meson.build b/glib/adbc-glib/meson.build
index 4496463c8..60d892ff3 100644
--- a/glib/adbc-glib/meson.build
+++ b/glib/adbc-glib/meson.build
@@ -31,6 +31,16 @@ definition_headers = files(
'statement.h',
)
+version_h_conf = configuration_data()
+version_h_conf.set('GADBC_VERSION', meson.project_version())
+version_h_conf.set('GADBC_VERSION_MAJOR', version_major)
+version_h_conf.set('GADBC_VERSION_MINOR', version_minor)
+version_h_conf.set('GADBC_VERSION_MICRO', version_micro)
+version_h = configure_file(input: 'version.h.in',
+ output: 'version.h',
+ configuration: version_h_conf)
+definition_headers += version_h
+
headers = definition_headers
headers += files(
'adbc-glib-raw.h',
@@ -41,16 +51,6 @@ headers += files(
'statement-raw.h',
)
-version_h_conf = configuration_data()
-version_h_conf.set('GADBC_VERSION', meson.project_version())
-version_h_conf.set('GADBC_VERSION_MAJOR', version_major)
-version_h_conf.set('GADBC_VERSION_MINOR', version_minor)
-version_h_conf.set('GADBC_VERSION_MICRO', version_micro)
-version_h = configure_file(input: 'version.h.in',
- output: 'version.h',
- configuration: version_h_conf)
-headers += version_h
-
enums = gnome.mkenums_simple('enum-types',
identifier_prefix: 'GADBC',
sources: definition_headers,
diff --git a/glib/adbc-glib/version.h.in b/glib/adbc-glib/version.h.in
index a7a883797..72b7aa80e 100644
--- a/glib/adbc-glib/version.h.in
+++ b/glib/adbc-glib/version.h.in
@@ -100,6 +100,15 @@
# define GADBC_UNAVAILABLE(major, minor) G_UNAVAILABLE(major, minor)
#endif
+/**
+ * GADBC_VERSION_1_0:
+ *
+ * You can use this macro value for compile time API version check.
+ *
+ * Since: 1.0.0
+ */
+#define GADBC_VERSION_1_0 G_ENCODE_VERSION(1, 0)
+
/**
* GADBC_VERSION_0_10:
*
@@ -174,6 +183,20 @@
#define GADBC_AVAILABLE_IN_ALL
+#if GADBC_VERSION_MIN_REQUIRED >= GADBC_VERSION_1_0
+# define GADBC_DEPRECATED_IN_1_0 GADBC_DEPRECATED
+# define GADBC_DEPRECATED_IN_1_0_FOR(function) GADBC_DEPRECATED_FOR(function)
+#else
+# define GADBC_DEPRECATED_IN_1_0
+# define GADBC_DEPRECATED_IN_1_0_FOR(function)
+#endif
+
+#if GADBC_VERSION_MAX_ALLOWED < GADBC_VERSION_1_0
+# define GADBC_AVAILABLE_IN_1_0 GADBC_UNAVAILABLE(1, 0)
+#else
+# define GADBC_AVAILABLE_IN_1_0
+#endif
+
#if GADBC_VERSION_MIN_REQUIRED >= GADBC_VERSION_0_10
# define GADBC_DEPRECATED_IN_0_10 GADBC_DEPRECATED
# define GADBC_DEPRECATED_IN_0_10_FOR(function)
GADBC_DEPRECATED_FOR(function)
diff --git a/glib/test/helper.rb b/glib/test/helper.rb
index 7054f9f31..a3a19e6c5 100644
--- a/glib/test/helper.rb
+++ b/glib/test/helper.rb
@@ -28,18 +28,28 @@ module Helper
omit(message)
end
+ def import_array_stream(c_abi_array_stream)
+ begin
+ reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ begin
+ yield(reader)
+ ensure
+ reader.unref
+ end
+ ensure
+ GLib.free(c_abi_array_stream)
+ end
+ end
+
def execute_statement(statement, need_result: true)
_, c_abi_array_stream, n_rows_affected = statement.execute(need_result)
- begin
- if need_result
- reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ if need_result
+ import_array_stream(c_abi_array_stream) do |reader|
table = reader.read_all
yield(table, n_rows_affected) if block_given?
- else
- yield(n_rows_affected) if block_given?
end
- ensure
- GLib.free(c_abi_array_stream) if need_result
+ else
+ yield(n_rows_affected) if block_given?
end
end
diff --git a/glib/test/helper/sandbox.rb b/glib/test/helper/sandbox.rb
new file mode 100644
index 000000000..f3ce2ea38
--- /dev/null
+++ b/glib/test/helper/sandbox.rb
@@ -0,0 +1,369 @@
+# 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.
+
+require "fileutils"
+require "socket"
+
+module Helper
+ class CommandRunError < StandardError
+ attr_reader :commane_line
+ attr_reader :output
+ attr_reader :error
+ def initialize(command_line, output, error)
+ @command_line = command_line
+ @output = output
+ @error = error
+ message = +"failed to run: "
+ message << command_line.join(" ")
+ message << "\n"
+ message << "output:\n"
+ message << output
+ message << "error:\n"
+ message << error
+ super(message)
+ end
+ end
+
+ module CommandRunnable
+ def spawn_process(*command_line)
+ env = {
+ "LC_ALL" => "C",
+ "PGCLIENTENCODING" => "UTF-8",
+ }
+ IO.pipe do |input_read, input_write|
+ input_write.sync = true
+ IO.pipe do |output_read, output_write|
+ IO.pipe do |error_read, error_write|
+ options = {
+ in: input_read,
+ out: output_write,
+ err: error_write,
+ }
+ pid = spawn(env, *command_line, options)
+ begin
+ input_read.close
+ output_write.close
+ error_write.close
+ yield(pid, input_write, output_read, error_read)
+ ensure
+ finished = false
+ begin
+ finished = !Process.waitpid(pid, Process::WNOHANG).nil?
+ rescue SystemCallError
+ # Finished
+ else
+ unless finished
+ Process.kill(:KILL, pid)
+ Process.waitpid(pid)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def read_command_output_all(input, initial_timeout: 1)
+ all_output = +""
+ timeout = initial_timeout
+ loop do
+ break unless IO.select([input], nil, nil, timeout)
+ all_output << read_command_output(input)
+ timeout = 0
+ end
+ all_output
+ end
+
+ def read_command_output(input)
+ return "" unless IO.select([input], nil, nil, 0)
+ begin
+ data = input.readpartial(4096).gsub(/\r\n/, "\n")
+ data.force_encoding("UTF-8")
+ data
+ rescue EOFError
+ ""
+ end
+ end
+
+ def run_command(*command_line)
+ spawn_process(*command_line) do |pid, input_write, output_read,
error_read|
+ output = +""
+ error = +""
+ status = nil
+ timeout = 1
+ if block_given?
+ begin
+ yield(input_write, output_read, output_read)
+ ensure
+ input_write.close unless input_write.closed?
+ end
+ end
+ loop do
+ readables, = IO.select([output_read, error_read], nil, nil, timeout)
+ if readables
+ timeout = 0
+ readables.each do |readable|
+ if readable == output_read
+ output << read_command_output(output_read)
+ else
+ error << read_command_output(error_read)
+ end
+ end
+ else
+ timeout = 1
+ end
+ _, status = Process.waitpid2(pid, Process::WNOHANG)
+ break if status
+ end
+ output << read_command_output(output_read)
+ error << read_command_output(error_read)
+ unless status.success?
+ raise CommandRunError.new(command_line, output, error)
+ end
+ [output, error]
+ end
+ end
+ end
+
+ class PostgreSQL
+ include CommandRunnable
+
+ attr_reader :dir
+ attr_reader :host
+ attr_reader :port
+ attr_reader :user
+ attr_reader :version
+ def initialize(base_dir)
+ @base_dir = base_dir
+ @dir = nil
+ @log_base_name = "postgresql.log"
+ @log_path = nil
+ @host = "127.0.0.1"
+ @port = nil
+ @user = "adbc"
+ @version = nil
+ @pid = nil
+ @running = false
+ end
+
+ def running?
+ @running
+ end
+
+ def initdb(db_path: "db",
+ port: 15432)
+ @dir = File.join(@base_dir, db_path)
+ @log_path = File.join(@dir, "log", @log_base_name)
+ @port = port
+ run_command("initdb",
+ "--locale", "C",
+ "--encoding", "UTF-8",
+ "--username", @user,
+ "-D", @dir)
+ postgresql_conf = File.join(@dir, "postgresql.conf")
+ File.open(postgresql_conf, "a") do |conf|
+ conf.puts("listen_addresses = '#{@host}'")
+ conf.puts("port = #{@port}")
+ conf.puts("logging_collector = on")
+ conf.puts("log_filename = '#{@log_base_name}'")
+ yield(conf) if block_given?
+ end
+ @version = Integer(File.read(File.join(@dir, "PG_VERSION")).chomp, 10)
+ end
+
+ def start
+ begin
+ run_command("pg_ctl", "start",
+ "-w",
+ "-D", @dir)
+ rescue => error
+ error.message << "\nPostgreSQL log:\n#{read_log}"
+ raise
+ end
+ loop do
+ begin
+ TCPSocket.open(@host, @port) do
+ end
+ rescue SystemCallError
+ sleep(0.1)
+ else
+ break
+ end
+ end
+ @running = true
+ pid_path = File.join(@dir, "postmaster.pid")
+ if File.exist?(pid_path)
+ first_line = File.readlines(pid_path, chomp: true)[0]
+ begin
+ @pid = Integer(first_line, 10)
+ rescue ArgumentError
+ end
+ end
+ end
+
+ def stop
+ return unless running?
+ begin
+ run_command("pg_ctl", "stop",
+ "-D", @dir)
+ rescue CommandRunError => error
+ if @pid
+ Process.kill(:KILL, @pid)
+ @pid = nil
+ @running = false
+ end
+ error.message << "\nPostgreSQL log:\n#{read_log}"
+ raise
+ else
+ @pid = nil
+ @running = false
+ end
+ end
+
+ def psql(db, *sqls, &block)
+ command_line = [
+ "psql",
+ "--host", @host,
+ "--port", @port.to_s,
+ "--username", @user,
+ "--dbname", db,
+ "--echo-all",
+ "--no-psqlrc",
+ ]
+ sqls.each do |sql|
+ command_line << "--command" << sql
+ end
+ output, error = run_command(*command_line, &block)
+ output = normalize_output(output)
+ [output, error]
+ end
+
+ def read_log
+ return "" unless File.exist?(@log_path)
+ File.read(@log_path)
+ end
+
+ private
+ def normalize_output(output)
+ normalized_output = +""
+ output.each_line do |line|
+ case line.chomp
+ when "SET", "CREATE EXTENSION"
+ next
+ end
+ normalized_output << line
+ end
+ normalized_output
+ end
+ end
+
+ module Sandbox
+ include CommandRunnable
+
+ class << self
+ def included(base)
+ base.module_eval do
+ setup :setup_tmp_dir
+ teardown :teardown_tmp_dir
+
+ setup :setup_db
+ teardown :teardown_db
+
+ setup :setup_postgres
+ teardown :teardown_postgres
+
+ setup :setup_test_db
+ teardown :teardown_test_db
+ end
+ end
+ end
+
+ def adbc_uri
+ host = @postgresql.host
+ port = @postgresql.port
+ user = @postgresql.user
+ "postgresql://#{host}:#{port}/#{@test_db_name}?user=#{user}"
+ end
+
+ def psql(db, *sqls, **options, &block)
+ @postgresql.psql(db, *sqls, **options, &block)
+ end
+
+ def run_sql(*sqls, **options, &block)
+ psql(@test_db_name, *sqls, **options, &block)
+ end
+
+ def setup_tmp_dir
+ memory_fs = "/dev/shm"
+ if File.exist?(memory_fs)
+ @tmp_dir = File.join(memory_fs, "adbc")
+ else
+ @tmp_dir = File.join(__dir__, "tmp")
+ end
+ FileUtils.rm_rf(@tmp_dir)
+ FileUtils.mkdir_p(@tmp_dir)
+ end
+
+ def teardown_tmp_dir
+ FileUtils.rm_rf(@tmp_dir)
+ end
+
+ def setup_db
+ @postgresql = PostgreSQL.new(@tmp_dir)
+ begin
+ @postgresql.initdb
+ rescue SystemCallError => error
+ omit("PostgreSQL isn't available: #{error}")
+ end
+ end
+
+ def teardown_db
+ end
+
+ def start_postgres
+ @postgresql.start
+ end
+
+ def stop_postgres
+ @postgresql.stop
+ end
+
+ def setup_postgres
+ start_postgres
+ end
+
+ def teardown_postgres
+ stop_postgres if @postgresql
+ end
+
+ def create_db(postgresql, db_name)
+ postgresql.psql("postgres", "CREATE DATABASE #{db_name}")
+ end
+
+ def setup_test_db
+ @test_db_name = "test"
+ create_db(@postgresql, @test_db_name)
+ result, = run_sql("SELECT oid FROM pg_catalog.pg_database " +
+ "WHERE datname = current_database()")
+ oid = result.lines[3].strip
+ @test_db_dir = File.join(@postgresql.dir, "base", oid)
+ end
+
+ def teardown_test_db
+ end
+ end
+end
diff --git a/glib/test/run.rb b/glib/test/run.rb
index 2685a1ad6..a05c4e917 100755
--- a/glib/test/run.rb
+++ b/glib/test/run.rb
@@ -17,6 +17,8 @@
# specific language governing permissions and limitations
# under the License.
+ENV["TEST_UNIT_MAX_DIFF_TARGET_STRING_SIZE"] ||= "10000"
+
require "pathname"
require "test-unit"
@@ -37,5 +39,6 @@ rescue GObjectIntrospection::RepositoryError => error
end
require_relative "helper"
+require_relative "helper/sandbox"
exit(Test::Unit::AutoRunner.run(true, test_dir.to_s))
diff --git a/glib/test/test-connection.rb b/glib/test/test-connection.rb
index c939a0d47..12e906301 100644
--- a/glib/test/test-connection.rb
+++ b/glib/test/test-connection.rb
@@ -17,18 +17,16 @@
class ConnectionTest < Test::Unit::TestCase
include Helper
+ include Helper::Sandbox
- def setup
+ setup def setup_database
@database = ADBC::Database.new
- @database.set_option("driver", "adbc_driver_sqlite")
- Dir.mktmpdir do |tmp_dir|
- database = File.join(tmp_dir, "test.sqlite3")
- @database.set_option("uri", database)
- @database.init
- open_connection do |connection|
- @connection = connection
- yield
- end
+ @database.set_option("driver", "adbc_driver_postgresql")
+ @database.set_option("uri", adbc_uri)
+ @database.init
+ open_connection do |connection|
+ @connection = connection
+ yield
end
end
@@ -42,7 +40,12 @@ class ConnectionTest < Test::Unit::TestCase
end
end
- def normalize_version(version)
+ 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
@@ -51,9 +54,10 @@ class ConnectionTest < Test::Unit::TestCase
info.collect do |code, value|
value = value.values[0] if value.is_a?(Hash)
case code
- when ADBC::Info::VENDOR_VERSION,
- ADBC::Info::DRIVER_ARROW_VERSION
- value = normalize_version(value)
+ 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
@@ -62,19 +66,17 @@ class ConnectionTest < Test::Unit::TestCase
sub_test_case("#get_info") do
def test_all
c_abi_array_stream = @connection.get_info
- begin
- reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ import_array_stream(c_abi_array_stream) do |reader|
table = reader.read_all
assert_equal([
- [ADBC::Info::VENDOR_NAME, "SQLite"],
+ [ADBC::Info::VENDOR_NAME, "PostgreSQL"],
[ADBC::Info::VENDOR_VERSION, "X.Y.Z"],
- [ADBC::Info::DRIVER_NAME, "ADBC SQLite Driver"],
+ [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))
- ensure
- GLib.free(c_abi_array_stream)
end
end
@@ -84,53 +86,50 @@ class ConnectionTest < Test::Unit::TestCase
ADBC::Info::DRIVER_NAME,
]
c_abi_array_stream = @connection.get_info(codes)
- begin
- reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ import_array_stream(c_abi_array_stream) do |reader|
table = reader.read_all
assert_equal([
- [ADBC::Info::VENDOR_NAME, "SQLite"],
- [ADBC::Info::DRIVER_NAME, "ADBC SQLite Driver"],
+ [ADBC::Info::VENDOR_NAME, "PostgreSQL"],
+ [ADBC::Info::DRIVER_NAME, "ADBC PostgreSQL Driver"],
],
normalize_info(table.raw_records))
- ensure
- GLib.free(c_abi_array_stream)
end
end
end
sub_test_case("#objects") do
- def setup
- super do
- 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)
- yield
- end
+ setup def setup_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)
+ yield
end
def get_objects(*args)
c_abi_array_stream = @connection.get_objects(*args)
- begin
- reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ import_array_stream(c_abi_array_stream) do |reader|
yield(reader.read_all)
- ensure
- GLib.free(c_abi_array_stream)
end
end
def test_catalogs_all
get_objects(:catalogs) do |table|
- assert_equal([["main", nil]],
+ assert_equal([
+ ["postgres", nil],
+ [@test_db_name, nil],
+ ["template1", nil],
+ ["template0", nil],
+ ],
table.raw_records)
end
end
def test_catalogs_match
- get_objects(:catalogs, "main") do |table|
- assert_equal([["main", nil]],
+ get_objects(:catalogs, @test_db_name) do |table|
+ assert_equal([[@test_db_name, nil]],
table.raw_records)
end
end
@@ -145,15 +144,18 @@ class ConnectionTest < Test::Unit::TestCase
def test_db_schemas_all
get_objects(:db_schemas) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => nil,
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -162,15 +164,18 @@ class ConnectionTest < Test::Unit::TestCase
def test_db_schemas_match
get_objects(:db_schemas, nil, nil) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => nil,
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -178,7 +183,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_db_schemas_not_match
get_objects(:db_schemas, nil, "nonexistent") do |table|
- assert_equal([["main", []]],
+ assert_equal([
+ ["postgres", []],
+ [@test_db_name, []],
+ ["template1", []],
+ ["template0", []],
+ ],
table.raw_records)
end
end
@@ -186,11 +196,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_tables_all
get_objects(:tables) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => nil,
@@ -202,6 +213,8 @@ class ConnectionTest < Test::Unit::TestCase
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -210,11 +223,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_tables_match
get_objects(:tables, nil, nil, "data") do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => nil,
@@ -226,6 +240,8 @@ class ConnectionTest < Test::Unit::TestCase
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -234,15 +250,18 @@ class ConnectionTest < Test::Unit::TestCase
def test_tables_not_match
get_objects(:tables, nil, nil, "nonexistent") do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [],
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -251,11 +270,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_table_types_all
get_objects(:tables) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => nil,
@@ -267,6 +287,8 @@ class ConnectionTest < Test::Unit::TestCase
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -275,11 +297,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_table_types_match
get_objects(:tables, nil, nil, nil, ["table", "view"]) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => nil,
@@ -291,6 +314,8 @@ class ConnectionTest < Test::Unit::TestCase
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -299,15 +324,18 @@ class ConnectionTest < Test::Unit::TestCase
def test_table_types_not_match
get_objects(:tables, nil, nil, nil, ["nonexistent"]) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [],
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -316,11 +344,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_column_all
get_objects(:all) do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => [
@@ -336,14 +365,14 @@ class ConnectionTest < Test::Unit::TestCase
"xdbc_decimal_digits" => nil,
"xdbc_is_autoincrement" => nil,
"xdbc_is_generatedcolumn" => nil,
- "xdbc_is_nullable" => "YES",
- "xdbc_nullable" => 1,
+ "xdbc_is_nullable" => nil,
+ "xdbc_nullable" => nil,
"xdbc_num_prec_radix" => nil,
"xdbc_scope_catalog" => nil,
"xdbc_scope_schema" => nil,
"xdbc_scope_table" => nil,
"xdbc_sql_data_type" => nil,
- "xdbc_type_name" => "INT",
+ "xdbc_type_name" => nil,
},
{
"column_name" => "string",
@@ -357,24 +386,26 @@ class ConnectionTest < Test::Unit::TestCase
"xdbc_decimal_digits" => nil,
"xdbc_is_autoincrement" => nil,
"xdbc_is_generatedcolumn" => nil,
- "xdbc_is_nullable" => "YES",
- "xdbc_nullable" => 1,
+ "xdbc_is_nullable" => nil,
+ "xdbc_nullable" => nil,
"xdbc_num_prec_radix" => nil,
"xdbc_scope_catalog" => nil,
"xdbc_scope_schema" => nil,
"xdbc_scope_table" => nil,
"xdbc_sql_data_type" => nil,
- "xdbc_type_name" => "TEXT",
+ "xdbc_type_name" => nil,
},
],
"table_constraints" => [],
"table_name" => "data",
"table_type" => "table",
- },
+ }
],
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -383,11 +414,12 @@ class ConnectionTest < Test::Unit::TestCase
def test_column_match
get_objects(:all, nil, nil, nil, nil, "number") do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => [
@@ -403,24 +435,26 @@ class ConnectionTest < Test::Unit::TestCase
"xdbc_decimal_digits" => nil,
"xdbc_is_autoincrement" => nil,
"xdbc_is_generatedcolumn" => nil,
- "xdbc_is_nullable" => "YES",
- "xdbc_nullable" => 1,
+ "xdbc_is_nullable" => nil,
+ "xdbc_nullable" => nil,
"xdbc_num_prec_radix" => nil,
"xdbc_scope_catalog" => nil,
"xdbc_scope_schema" => nil,
"xdbc_scope_table" => nil,
"xdbc_sql_data_type" => nil,
- "xdbc_type_name" => "INT",
+ "xdbc_type_name" => nil,
},
],
"table_constraints" => [],
"table_name" => "data",
"table_type" => "table",
- },
+ }
],
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -429,22 +463,25 @@ class ConnectionTest < Test::Unit::TestCase
def test_column_not_match
get_objects(:all, nil, nil, nil, nil, "nonexistent") do |table|
assert_equal([
+ ["postgres", []],
[
- "main",
+ @test_db_name,
[
{
- "db_schema_name" => "",
+ "db_schema_name" => "public",
"db_schema_tables" => [
{
"table_columns" => [],
"table_constraints" => [],
"table_name" => "data",
"table_type" => "table",
- },
+ }
],
},
],
],
+ ["template1", []],
+ ["template0", []],
],
table.raw_records)
end
@@ -462,7 +499,7 @@ class ConnectionTest < Test::Unit::TestCase
c_abi_schema = @connection.get_table_schema(nil, nil, "data")
begin
schema = Arrow::Schema.import(c_abi_schema)
- assert_equal(Arrow::Schema.new(number: :int64,
+ assert_equal(Arrow::Schema.new(number: :int32,
string: :string),
schema)
ensure
@@ -472,18 +509,22 @@ class ConnectionTest < Test::Unit::TestCase
def test_table_types
c_abi_array_stream = @connection.table_types
- begin
- reader = Arrow::RecordBatchReader.import(c_abi_array_stream)
+ import_array_stream(c_abi_array_stream) do |reader|
table = reader.read_all
fields = [
Arrow::Field.new("table_type", :string, false),
]
schema = Arrow::Schema.new(fields)
- table_types = Arrow::StringArray.new(["table", "view"])
+ table_types = Arrow::StringArray.new([
+ "partitioned_table",
+ "foreign_table",
+ "toast_table",
+ "materialized_view",
+ "view",
+ "table",
+ ])
assert_equal(Arrow::Table.new(schema, [table_types]),
table)
- ensure
- GLib.free(c_abi_array_stream)
end
end
@@ -492,7 +533,7 @@ class ConnectionTest < Test::Unit::TestCase
message =
"[adbc][connection][set-option]" +
"[NOT_IMPLEMENTED][0] " +
- "[SQLite] Unknown connection option adbc.connection.readonly='false'"
+ "[libpq] Unknown option adbc.connection.readonly"
assert_raise(ADBC::Error::NotImplemented.new(message)) do
connection.read_only = false
end
@@ -504,15 +545,175 @@ class ConnectionTest < Test::Unit::TestCase
message =
"[adbc][connection][set-option]" +
"[NOT_IMPLEMENTED][0] " +
- "[SQLite] Unknown connection option " +
- "adbc.connection.transaction.isolation_level=" +
- "'adbc.connection.transaction.isolation.linearizable'"
+ "[libpq] Unknown option " +
+ "adbc.connection.transaction.isolation_level"
assert_raise(ADBC::Error::NotImplemented.new(message)) do
connection.isolation_level = :linearizable
end
end
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)")
+ run_sql("CREATE TABLE public.data2 (name text)")
+ run_sql("INSERT INTO public.data2 VALUES ('hello'), (NULL)")
+ run_sql("ANALYZE")
+ c_abi_array_stream = @connection.get_statistics(nil, "public", nil, true)
+ import_array_stream(c_abi_array_stream) do |reader|
+ 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
+ end
+
+ def test_schema_table
+ 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("ANALYZE")
+ c_abi_array_stream =
+ @connection.get_statistics(nil, "public", "data1", true)
+ import_array_stream(c_abi_array_stream) do |reader|
+ 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,
+ },
+ ],
+ },
+ ],
+ ],
+ ],
+ normalize_statistics(table.raw_records)
+ )
+ end
+ end
+ end
+
def test_commit
open_connection do |connection|
execute_sql(connection,
@@ -531,7 +732,7 @@ class ConnectionTest < Test::Unit::TestCase
open_connection do |other_connection|
execute_sql(other_connection, "SELECT * FROM data") do |table,|
expected = {
- number: Arrow::Int64Array.new([1]),
+ number: Arrow::Int32Array.new([1]),
string: Arrow::StringArray.new(["hello"]),
}
assert_equal(Arrow::Table.new(expected),
@@ -542,7 +743,7 @@ class ConnectionTest < Test::Unit::TestCase
open_connection do |other_connection|
execute_sql(other_connection, "SELECT * FROM data") do |table,|
expected = {
- number: Arrow::Int64Array.new([1, 2]),
+ number: Arrow::Int32Array.new([1, 2]),
string: Arrow::StringArray.new(["hello", "world"]),
}
assert_equal(Arrow::Table.new(expected),
@@ -570,7 +771,7 @@ class ConnectionTest < Test::Unit::TestCase
open_connection do |other_connection|
execute_sql(other_connection, "SELECT * FROM data") do |table,|
expected = {
- number: Arrow::Int64Array.new([1]),
+ number: Arrow::Int32Array.new([1]),
string: Arrow::StringArray.new(["hello"]),
}
assert_equal(Arrow::Table.new(expected),
@@ -581,7 +782,7 @@ class ConnectionTest < Test::Unit::TestCase
open_connection do |other_connection|
execute_sql(other_connection, "SELECT * FROM data") do |table,|
expected = {
- number: Arrow::Int64Array.new([1]),
+ number: Arrow::Int32Array.new([1]),
string: Arrow::StringArray.new(["hello"]),
}
assert_equal(Arrow::Table.new(expected),
diff --git a/ci/conda_env_glib.txt b/glib/test/test-statistic-key.rb
similarity index 79%
copy from ci/conda_env_glib.txt
copy to glib/test/test-statistic-key.rb
index cd937ee22..d3d0db9b3 100644
--- a/ci/conda_env_glib.txt
+++ b/glib/test/test-statistic-key.rb
@@ -15,8 +15,11 @@
# specific language governing permissions and limitations
# under the License.
-arrow-c-glib
-glib
-gobject-introspection
-meson
-ruby
+class StatisticKeyTest < Test::Unit::TestCase
+ include Helper
+
+ def test_to_string
+ assert_equal("adbc.statistic.null_count",
+ ADBC::StatisticKey.to_string(:null_count))
+ end
+end