You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by sh...@apache.org on 2018/11/11 02:20:47 UTC

[arrow] branch master updated: ARROW-3748: [GLib] Add GArrowCSVReader

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

shiro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 455cde1  ARROW-3748: [GLib] Add GArrowCSVReader
455cde1 is described below

commit 455cde1096d3436fbaf3f8fbfe39503a3b1c9326
Author: Kouhei Sutou <ko...@clear-code.com>
AuthorDate: Sun Nov 11 11:20:23 2018 +0900

    ARROW-3748: [GLib] Add GArrowCSVReader
    
    Author: Kouhei Sutou <ko...@clear-code.com>
    
    Closes #2934 from kou/glib-csv and squashes the following commits:
    
    e6b70787 <Kouhei Sutou>  Add GArrowCSVReader
---
 c_glib/arrow-glib/reader.cpp              | 566 +++++++++++++++++++++++++++++-
 c_glib/arrow-glib/reader.h                |  31 ++
 c_glib/arrow-glib/reader.hpp              |   6 +
 c_glib/doc/arrow-glib/arrow-glib-docs.xml |   4 +
 c_glib/test/test-csv-reader.rb            |  59 ++++
 5 files changed, 657 insertions(+), 9 deletions(-)

diff --git a/c_glib/arrow-glib/reader.cpp b/c_glib/arrow-glib/reader.cpp
index 296b911..5253a45 100644
--- a/c_glib/arrow-glib/reader.cpp
+++ b/c_glib/arrow-glib/reader.cpp
@@ -51,6 +51,9 @@ G_BEGIN_DECLS
  *
  * #GArrowFeatherFileReader is a class for reading columns in Feather
  * file format from input.
+ *
+ * #GArrowCSVReader is a class for reading table in CSV format from
+ * input.
  */
 
 typedef struct GArrowRecordBatchReaderPrivate_ {
@@ -888,6 +891,541 @@ garrow_feather_file_reader_read_names(GArrowFeatherFileReader *reader,
   }
 }
 
+
+typedef struct GArrowCSVReadOptionsPrivate_ {
+  arrow::MemoryPool *pool;
+  arrow::csv::ReadOptions read_options;
+  arrow::csv::ParseOptions parse_options;
+  arrow::csv::ConvertOptions convert_options;
+} GArrowCSVReadOptionsPrivate;
+
+enum {
+  PROP_POOL = 1,
+  PROP_USE_THREADS,
+  PROP_BLOCK_SIZE,
+  PROP_DELIMITER,
+  PROP_IS_QUOTED,
+  PROP_QUOTE_CHARACTER,
+  PROP_IS_DOUBLE_QUOTED,
+  PROP_IS_ESCAPED,
+  PROP_ESCAPE_CHARACTER,
+  PROP_ALLOW_NEWLINES_IN_VALUES,
+  PROP_IGNORE_EMPTY_LINES,
+  PROP_N_HEADER_ROWS,
+  PROP_CHECK_UTF8
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(GArrowCSVReadOptions,
+                           garrow_csv_read_options,
+                           G_TYPE_OBJECT)
+
+#define GARROW_CSV_READ_OPTIONS_GET_PRIVATE(object) \
+  static_cast<GArrowCSVReadOptionsPrivate *>(       \
+    garrow_csv_read_options_get_instance_private(   \
+      GARROW_CSV_READ_OPTIONS(object)))
+
+static void
+garrow_csv_read_options_set_property(GObject *object,
+                                     guint prop_id,
+                                     const GValue *value,
+                                     GParamSpec *pspec)
+{
+  auto priv = GARROW_CSV_READ_OPTIONS_GET_PRIVATE(object);
+
+  switch (prop_id) {
+  case PROP_POOL:
+    priv->pool = static_cast<arrow::MemoryPool *>(g_value_get_pointer(value));
+    break;
+  case PROP_USE_THREADS:
+    priv->read_options.use_threads = g_value_get_boolean(value);
+    break;
+  case PROP_BLOCK_SIZE:
+    priv->read_options.block_size = g_value_get_int(value);
+    break;
+  case PROP_DELIMITER:
+    priv->parse_options.delimiter = g_value_get_schar(value);
+    break;
+  case PROP_IS_QUOTED:
+    priv->parse_options.quoting = g_value_get_boolean(value);
+    break;
+  case PROP_QUOTE_CHARACTER:
+    priv->parse_options.quote_char = g_value_get_schar(value);
+    break;
+  case PROP_IS_DOUBLE_QUOTED:
+    priv->parse_options.double_quote = g_value_get_boolean(value);
+    break;
+  case PROP_IS_ESCAPED:
+    priv->parse_options.escaping = g_value_get_boolean(value);
+    break;
+  case PROP_ESCAPE_CHARACTER:
+    priv->parse_options.escape_char = g_value_get_schar(value);
+    break;
+  case PROP_ALLOW_NEWLINES_IN_VALUES:
+    priv->parse_options.newlines_in_values = g_value_get_boolean(value);
+    break;
+  case PROP_IGNORE_EMPTY_LINES:
+    priv->parse_options.ignore_empty_lines = g_value_get_boolean(value);
+    break;
+  case PROP_N_HEADER_ROWS:
+    priv->parse_options.header_rows = g_value_get_uint(value);
+    break;
+  case PROP_CHECK_UTF8:
+    priv->convert_options.check_utf8 = g_value_get_boolean(value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_csv_read_options_get_property(GObject *object,
+                                     guint prop_id,
+                                     GValue *value,
+                                     GParamSpec *pspec)
+{
+  auto priv = GARROW_CSV_READ_OPTIONS_GET_PRIVATE(object);
+
+  switch (prop_id) {
+  case PROP_USE_THREADS:
+    g_value_set_boolean(value, priv->read_options.use_threads);
+    break;
+  case PROP_BLOCK_SIZE:
+    g_value_set_int(value, priv->read_options.block_size);
+    break;
+  case PROP_DELIMITER:
+    g_value_set_schar(value, priv->parse_options.delimiter);
+    break;
+  case PROP_IS_QUOTED:
+    g_value_set_boolean(value, priv->parse_options.quoting);
+    break;
+  case PROP_QUOTE_CHARACTER:
+    g_value_set_schar(value, priv->parse_options.quote_char);
+    break;
+  case PROP_IS_DOUBLE_QUOTED:
+    g_value_set_boolean(value, priv->parse_options.double_quote);
+    break;
+  case PROP_IS_ESCAPED:
+    g_value_set_boolean(value, priv->parse_options.escaping);
+    break;
+  case PROP_ESCAPE_CHARACTER:
+    g_value_set_schar(value, priv->parse_options.escape_char);
+    break;
+  case PROP_ALLOW_NEWLINES_IN_VALUES:
+    g_value_set_boolean(value, priv->parse_options.newlines_in_values);
+    break;
+  case PROP_IGNORE_EMPTY_LINES:
+    g_value_set_boolean(value, priv->parse_options.ignore_empty_lines);
+    break;
+  case PROP_N_HEADER_ROWS:
+    g_value_set_uint(value, priv->parse_options.header_rows);
+    break;
+  case PROP_CHECK_UTF8:
+    g_value_set_boolean(value, priv->convert_options.check_utf8);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_csv_read_options_init(GArrowCSVReadOptions *object)
+{
+  auto priv = GARROW_CSV_READ_OPTIONS_GET_PRIVATE(object);
+  priv->pool = arrow::default_memory_pool();
+  priv->read_options = arrow::csv::ReadOptions::Defaults();
+  priv->parse_options = arrow::csv::ParseOptions::Defaults();
+  priv->convert_options = arrow::csv::ConvertOptions::Defaults();
+}
+
+static void
+garrow_csv_read_options_class_init(GArrowCSVReadOptionsClass *klass)
+{
+  GParamSpec *spec;
+
+  auto gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->set_property = garrow_csv_read_options_set_property;
+  gobject_class->get_property = garrow_csv_read_options_get_property;
+
+  spec = g_param_spec_pointer("pool",
+                              "Pool",
+                              "The raw arrow::MemoryPool *",
+                              static_cast<GParamFlags>(G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property(gobject_class, PROP_POOL, spec);
+
+  auto read_options = arrow::csv::ReadOptions::Defaults();
+
+  /**
+   * GArrowCSVReadOptions:use-threads:
+   *
+   * Whether to use the global CPU thread pool.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("use-threads",
+                              "Use threads",
+                              "Whether to use the global CPU thread pool",
+                              read_options.use_threads,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_USE_THREADS, spec);
+
+  /**
+   * GArrowCSVReadOptions:block-size:
+   *
+   * Block size we request from the IO layer; also determines the size
+   * of chunks when #GArrowCSVReadOptions:use-threads is %TRUE.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_int("block-size",
+                          "Block size",
+                          "Block size we request from the IO layer; "
+                          "also determines the size of chunks "
+                          "when ::use-threads is %TRUE",
+                          0,
+                          G_MAXINT,
+                          read_options.block_size,
+                          static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_BLOCK_SIZE, spec);
+
+
+  auto parse_options = arrow::csv::ParseOptions::Defaults();
+
+  /**
+   * GArrowCSVReadOptions:delimiter:
+   *
+   * Field delimiter character.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_char("delimiter",
+                           "Delimiter",
+                           "Field delimiter character",
+                           0,
+                           G_MAXINT8,
+                           parse_options.delimiter,
+                           static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_DELIMITER, spec);
+
+  /**
+   * GArrowCSVReadOptions:is-quoted:
+   *
+   * Whether quoting is used.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("is-quoted",
+                              "Is quoted",
+                              "Whether quoting is used",
+                              parse_options.quoting,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_IS_QUOTED, spec);
+
+  /**
+   * GArrowCSVReadOptions:quote-character:
+   *
+   * Quoting character. This is used only when
+   * #GArrowCSVReadOptions:is-quoted is %TRUE.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_char("quote-character",
+                           "Quote character",
+                           "Quoting character",
+                           0,
+                           G_MAXINT8,
+                           parse_options.quote_char,
+                           static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_QUOTE_CHARACTER, spec);
+
+  /**
+   * GArrowCSVReadOptions:is-double-quoted:
+   *
+   * Whether a quote inside a value is double quoted.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("is-double-quoted",
+                              "Is double quoted",
+                              "Whether a quote inside a value is double quoted",
+                              parse_options.double_quote,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_IS_DOUBLE_QUOTED, spec);
+
+  /**
+   * GArrowCSVReadOptions:is-escaped:
+   *
+   * Whether escaping is used.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("is-escaped",
+                              "Is escaped",
+                              "Whether escaping is used",
+                              parse_options.escaping,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_IS_ESCAPED, spec);
+
+  /**
+   * GArrowCSVReadOptions:escape-character:
+   *
+   * Escaping character. This is used only when
+   * #GArrowCSVReadOptions:is-escaped is %TRUE.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_char("escape-character",
+                           "Escape character",
+                           "Escaping character",
+                           0,
+                           G_MAXINT8,
+                           parse_options.escape_char,
+                           static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_ESCAPE_CHARACTER, spec);
+
+  /**
+   * GArrowCSVReadOptions:allow-newlines-in-values:
+   *
+   * Whether values are allowed to contain CR (0x0d) and LF (0x0a) characters.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("allow-newlines-in-values",
+                              "Allow newlines in values",
+                              "Whether values are allowed to contain "
+                              "CR (0x0d) and LF (0x0a) characters.",
+                              parse_options.newlines_in_values,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class,
+                                  PROP_ALLOW_NEWLINES_IN_VALUES,
+                                  spec);
+
+  /**
+   * GArrowCSVReadOptions:ignore-empty-lines:
+   *
+   * Whether empty lines are ignored. If %FALSE, an empty line
+   * represents a simple empty value (assuming a one-column CSV file).
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("ignore-empty-lines",
+                              "Ignore empty lines",
+                              "Whether empty lines are ignored. "
+                              "If %FALSE, an empty line represents "
+                              "a simple empty value "
+                              "(assuming a one-column CSV file).",
+                              parse_options.ignore_empty_lines,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class,
+                                  PROP_IGNORE_EMPTY_LINES,
+                                  spec);
+
+  /**
+   * GArrowCSVReadOptions:n-header-rows:
+   *
+   * The number of header rows to skip (including the first row
+   * containing column names)
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_uint("n-header-rows",
+                           "N header rows",
+                           "The number of header rows to skip "
+                           "(including the first row containing column names",
+                           0,
+                           G_MAXUINT,
+                           parse_options.header_rows,
+                           static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class,
+                                  PROP_N_HEADER_ROWS,
+                                  spec);
+
+  auto convert_options = arrow::csv::ConvertOptions::Defaults();
+
+  /**
+   * GArrowCSVReadOptions:check-utf8:
+   *
+   * Whether to check UTF8 validity of string columns.
+   *
+   * Since: 0.12.0
+   */
+  spec = g_param_spec_boolean("check-utf8",
+                              "Check UTF8",
+                              "Whether to check UTF8 validity of string columns",
+                              convert_options.check_utf8,
+                              static_cast<GParamFlags>(G_PARAM_READWRITE));
+  g_object_class_install_property(gobject_class, PROP_CHECK_UTF8, spec);
+}
+
+/**
+ * garrow_csv_read_options_new:
+ *
+ * Returns: A newly created #GArrowCSVReadOptions.
+ *
+ * Since: 0.12.0
+ */
+GArrowCSVReadOptions *
+garrow_csv_read_options_new(void)
+{
+  auto csv_read_options = g_object_new(GARROW_TYPE_CSV_READ_OPTIONS,
+                                       "pool", arrow::default_memory_pool(),
+                                       NULL);
+  return GARROW_CSV_READ_OPTIONS(csv_read_options);
+}
+
+
+typedef struct GArrowCSVReaderPrivate_ {
+  std::shared_ptr<arrow::csv::TableReader> reader;
+} GArrowCSVReaderPrivate;
+
+enum {
+  PROP_CSV_TABLE_READER = 1
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(GArrowCSVReader,
+                           garrow_csv_reader,
+                           G_TYPE_OBJECT)
+
+#define GARROW_CSV_READER_GET_PRIVATE(object)   \
+  static_cast<GArrowCSVReaderPrivate *>(        \
+    garrow_csv_reader_get_instance_private(     \
+      GARROW_CSV_READER(object)))
+
+static void
+garrow_csv_reader_dispose(GObject *object)
+{
+  auto priv = GARROW_CSV_READER_GET_PRIVATE(object);
+
+  priv->reader = nullptr;
+
+  G_OBJECT_CLASS(garrow_csv_reader_parent_class)->dispose(object);
+}
+
+static void
+garrow_csv_reader_set_property(GObject *object,
+                               guint prop_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+  auto priv = GARROW_CSV_READER_GET_PRIVATE(object);
+
+  switch (prop_id) {
+  case PROP_CSV_TABLE_READER:
+    priv->reader =
+      *static_cast<std::shared_ptr<arrow::csv::TableReader> *>(g_value_get_pointer(value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_csv_reader_get_property(GObject *object,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+  switch (prop_id) {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+garrow_csv_reader_init(GArrowCSVReader *object)
+{
+}
+
+static void
+garrow_csv_reader_class_init(GArrowCSVReaderClass *klass)
+{
+  GParamSpec *spec;
+
+  auto gobject_class = G_OBJECT_CLASS(klass);
+
+  gobject_class->dispose      = garrow_csv_reader_dispose;
+  gobject_class->set_property = garrow_csv_reader_set_property;
+  gobject_class->get_property = garrow_csv_reader_get_property;
+
+  spec = g_param_spec_pointer("csv-table-reader",
+                              "CSV table reader",
+                              "The raw std::shared<arrow::csv::TableReader> *",
+                              static_cast<GParamFlags>(G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property(gobject_class, PROP_CSV_TABLE_READER, spec);
+}
+
+/**
+ * garrow_csv_reader_new:
+ * @input: The input to be read.
+ * @options: (nullable): A #GArrowCSVReadOptions.
+ * @error: (nullable): Return location for a #GError or %NULL.
+ *
+ * Returns: (nullable): A newly created #GArrowCSVReader or %NULL on error.
+ *
+ * Since: 0.12.0
+ */
+GArrowCSVReader *
+garrow_csv_reader_new(GArrowInputStream *input,
+                      GArrowCSVReadOptions *options,
+                      GError **error)
+{
+  auto arrow_input = garrow_input_stream_get_raw(input);
+  arrow::Status status;
+  std::shared_ptr<arrow::csv::TableReader> arrow_reader;
+  if (options) {
+    auto options_priv = GARROW_CSV_READ_OPTIONS_GET_PRIVATE(options);
+    status = arrow::csv::TableReader::Make(options_priv->pool,
+                                           arrow_input,
+                                           options_priv->read_options,
+                                           options_priv->parse_options,
+                                           options_priv->convert_options,
+                                           &arrow_reader);
+  } else {
+    status =
+      arrow::csv::TableReader::Make(arrow::default_memory_pool(),
+                                    arrow_input,
+                                    arrow::csv::ReadOptions::Defaults(),
+                                    arrow::csv::ParseOptions::Defaults(),
+                                    arrow::csv::ConvertOptions::Defaults(),
+                                    &arrow_reader);
+  }
+
+  if (garrow_error_check(error, status, "[csv-reader][new]")) {
+    return garrow_csv_reader_new_raw(&arrow_reader);
+  } else {
+    return NULL;
+  }
+}
+
+/**
+ * garrow_csv_reader_read:
+ * @reader: A #GArrowCSVReader.
+ * @error: (nullable): Return location for a #GError or %NULL.
+ *
+ * Returns: (nullable) (transfer full): A read #GArrowTable or %NULL on error.
+ *
+ * Since: 0.12.0
+ */
+GArrowTable *
+garrow_csv_reader_read(GArrowCSVReader *reader,
+                       GError **error)
+{
+  auto arrow_reader = garrow_csv_reader_get_raw(reader);
+  std::shared_ptr<arrow::Table> arrow_table;
+  auto status = arrow_reader->Read(&arrow_table);
+  if (garrow_error_check(error, status, "[csv-reader][read]")) {
+    return garrow_table_new_raw(&arrow_table);
+  } else {
+    return NULL;
+  }
+}
+
 G_END_DECLS
 
 GArrowRecordBatchReader *
@@ -903,9 +1441,7 @@ garrow_record_batch_reader_new_raw(std::shared_ptr<arrow::ipc::RecordBatchReader
 std::shared_ptr<arrow::ipc::RecordBatchReader>
 garrow_record_batch_reader_get_raw(GArrowRecordBatchReader *reader)
 {
-  GArrowRecordBatchReaderPrivate *priv;
-
-  priv = GARROW_RECORD_BATCH_READER_GET_PRIVATE(reader);
+  auto priv = GARROW_RECORD_BATCH_READER_GET_PRIVATE(reader);
   return priv->record_batch_reader;
 }
 
@@ -944,9 +1480,7 @@ garrow_record_batch_file_reader_new_raw(std::shared_ptr<arrow::ipc::RecordBatchF
 std::shared_ptr<arrow::ipc::RecordBatchFileReader>
 garrow_record_batch_file_reader_get_raw(GArrowRecordBatchFileReader *reader)
 {
-  GArrowRecordBatchFileReaderPrivate *priv;
-
-  priv = GARROW_RECORD_BATCH_FILE_READER_GET_PRIVATE(reader);
+  auto priv = GARROW_RECORD_BATCH_FILE_READER_GET_PRIVATE(reader);
   return priv->record_batch_file_reader;
 }
 
@@ -964,8 +1498,22 @@ garrow_feather_file_reader_new_raw(arrow::ipc::feather::TableReader *arrow_reade
 arrow::ipc::feather::TableReader *
 garrow_feather_file_reader_get_raw(GArrowFeatherFileReader *reader)
 {
-  GArrowFeatherFileReaderPrivate *priv;
-
-  priv = GARROW_FEATHER_FILE_READER_GET_PRIVATE(reader);
+  auto priv = GARROW_FEATHER_FILE_READER_GET_PRIVATE(reader);
   return priv->feather_table_reader;
 }
+
+GArrowCSVReader *
+garrow_csv_reader_new_raw(std::shared_ptr<arrow::csv::TableReader> *arrow_reader)
+{
+  auto reader = GARROW_CSV_READER(g_object_new(GARROW_TYPE_CSV_READER,
+                                               "csv-table-reader", arrow_reader,
+                                               NULL));
+  return reader;
+}
+
+std::shared_ptr<arrow::csv::TableReader>
+garrow_csv_reader_get_raw(GArrowCSVReader *reader)
+{
+  auto priv = GARROW_CSV_READER_GET_PRIVATE(reader);
+  return priv->reader;
+}
diff --git a/c_glib/arrow-glib/reader.h b/c_glib/arrow-glib/reader.h
index b043ec1..d1a3947 100644
--- a/c_glib/arrow-glib/reader.h
+++ b/c_glib/arrow-glib/reader.h
@@ -243,4 +243,35 @@ garrow_feather_file_reader_read_names(GArrowFeatherFileReader *reader,
                                       guint n_names,
                                       GError **error);
 
+#define GARROW_TYPE_CSV_READ_OPTIONS (garrow_csv_read_options_get_type())
+G_DECLARE_DERIVABLE_TYPE(GArrowCSVReadOptions,
+                         garrow_csv_read_options,
+                         GARROW,
+                         CSV_READ_OPTIONS,
+                         GObject)
+struct _GArrowCSVReadOptionsClass
+{
+  GObjectClass parent_class;
+};
+
+GArrowCSVReadOptions *garrow_csv_read_options_new(void);
+
+#define GARROW_TYPE_CSV_READER (garrow_csv_reader_get_type())
+G_DECLARE_DERIVABLE_TYPE(GArrowCSVReader,
+                         garrow_csv_reader,
+                         GARROW,
+                         CSV_READER,
+                         GObject)
+struct _GArrowCSVReaderClass
+{
+  GObjectClass parent_class;
+};
+
+GArrowCSVReader *garrow_csv_reader_new(GArrowInputStream *input,
+                                       GArrowCSVReadOptions *options,
+                                       GError **error);
+GArrowTable *garrow_csv_reader_read(GArrowCSVReader *reader,
+                                    GError **error);
+
+
 G_END_DECLS
diff --git a/c_glib/arrow-glib/reader.hpp b/c_glib/arrow-glib/reader.hpp
index 3e1135e..4f85e49 100644
--- a/c_glib/arrow-glib/reader.hpp
+++ b/c_glib/arrow-glib/reader.hpp
@@ -20,6 +20,7 @@
 #pragma once
 
 #include <arrow/api.h>
+#include <arrow/csv/api.h>
 #include <arrow/ipc/api.h>
 #include <arrow/ipc/feather.h>
 
@@ -37,3 +38,8 @@ std::shared_ptr<arrow::ipc::RecordBatchFileReader> garrow_record_batch_file_read
 
 GArrowFeatherFileReader *garrow_feather_file_reader_new_raw(arrow::ipc::feather::TableReader *arrow_reader);
 arrow::ipc::feather::TableReader *garrow_feather_file_reader_get_raw(GArrowFeatherFileReader *reader);
+
+GArrowCSVReader *
+garrow_csv_reader_new_raw(std::shared_ptr<arrow::csv::TableReader> *arrow_reader);
+std::shared_ptr<arrow::csv::TableReader>
+garrow_csv_reader_get_raw(GArrowCSVReader *reader);
diff --git a/c_glib/doc/arrow-glib/arrow-glib-docs.xml b/c_glib/doc/arrow-glib/arrow-glib-docs.xml
index 462f42e..948a321 100644
--- a/c_glib/doc/arrow-glib/arrow-glib-docs.xml
+++ b/c_glib/doc/arrow-glib/arrow-glib-docs.xml
@@ -159,6 +159,10 @@
     <title>Index of deprecated API</title>
     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-0-12-0" role="0.12.0">
+    <title>Index of new symbols in 0.12.0</title>
+    <xi:include href="xml/api-index-0.12.0.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-0-11-0" role="0.11.0">
     <title>Index of new symbols in 0.11.0</title>
     <xi:include href="xml/api-index-0.11.0.xml"><xi:fallback /></xi:include>
diff --git a/c_glib/test/test-csv-reader.rb b/c_glib/test/test-csv-reader.rb
new file mode 100644
index 0000000..12897a8
--- /dev/null
+++ b/c_glib/test/test-csv-reader.rb
@@ -0,0 +1,59 @@
+# 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 TestCSVReader < Test::Unit::TestCase
+  include Helper::Buildable
+  include Helper::Omittable
+
+  sub_test_case("#read") do
+    def open_input(csv)
+      buffer = Arrow::Buffer.new(csv)
+      Arrow::BufferInputStream.new(buffer)
+    end
+
+    def test_default
+      require_gi(1, 42, 0)
+      table = Arrow::CSVReader.new(open_input(<<-CSV))
+message,count
+"Start",2
+"Shutdown",9
+      CSV
+      columns = {
+        "message" => build_string_array(["Start", "Shutdown"]),
+        "count" => build_int64_array([2, 9]),
+      }
+      assert_equal(build_table(columns),
+                   table.read)
+    end
+
+    def test_options
+      options = Arrow::CSVReadOptions.new
+      options.quoted = false
+      table = Arrow::CSVReader.new(open_input(<<-CSV), options)
+message,count
+"Start",2
+"Shutdown",9
+      CSV
+      columns = {
+        "message" => build_string_array(["\"Start\"", "\"Shutdown\""]),
+        "count" => build_int64_array([2, 9]),
+      }
+      assert_equal(build_table(columns),
+                   table.read)
+    end
+  end
+end