[Groonga-commit] groonga/groonga at e19bfde [master] Support window function

Back to archive index

Kouhei Sutou null+****@clear*****
Wed May 18 00:22:11 JST 2016


Kouhei Sutou	2016-05-18 00:22:11 +0900 (Wed, 18 May 2016)

  New Revision: e19bfde7bcf9cee483dc9c4267afff847ce14042
  https://github.com/groonga/groonga/commit/e19bfde7bcf9cee483dc9c4267afff847ce14042

  Message:
    Support window function
    
    row_number() is the only available window function for now.
    
    Here is an usage:
    
        table_create Items TABLE_HASH_KEY ShortText
        column_create Items price COLUMN_SCALAR UInt32
    
        load --table Items
        [
        {"_key": "item1", "price": 666},
        {"_key": "item2", "price": 999},
        {"_key": "item3", "price": 777},
        {"_key": "item4", "price": 111},
        {"_key": "item5", "price": 333},
        {"_key": "item6", "price": 222}
        ]
    
        select Items \
          --column[nth_row].stage initial \
          --column[nth_row].value 'row_number()' \
          --column[nth_row].type UInt32 \
          --column[nth_row].window.sort_keys -price \
          --output_columns '_key, price, nth_row'
        #[
        #  [
        #    0,
        #    0.0,
        #    0.0
        #  ],
        #  [
        #    [
        #      [
        #        6
        #      ],
        #      [
        #        [
        #          "_key",
        #          "ShortText"
        #        ],
        #        [
        #          "price",
        #          "UInt32"
        #        ],
        #        [
        #          "nth_row",
        #          "UInt32"
        #        ]
        #      ],
        #      [
        #        "item1",
        #        666,
        #        3
        #      ],
        #      [
        #        "item2",
        #        999,
        #        1
        #      ],
        #      [
        #        "item3",
        #        777,
        #        2
        #      ],
        #      [
        #        "item4",
        #        111,
        #        6
        #      ],
        #      [
        #        "item5",
        #        333,
        #        4
        #      ],
        #      [
        #        "item6",
        #        222,
        #        5
        #      ]
        #    ]
        #  ]
        #]
    
    New APIs:
    
      * GRN_WINDOW_FUNCTION_ERROR
      * GRN_PROC_WINDOW_FUNCTION
      * grn_obj_is_window_function_proc()
      * grn_window_direction
      * grn_window
      * grn_window_next()
      * grn_window_rewind()
      * grn_window_set_direction()
      * grn_window_get_table()
      * grn_window_definition
      * grn_window_function_func
      * grn_window_function_create()
      * grn_table_apply_window_function()

  Added files:
    include/groonga/window_function.h
    lib/window_function.c
    lib/window_functions.c
    test/command/suite/select/column/window_function/row_number/ascending.expected
    test/command/suite/select/column/window_function/row_number/ascending.test
    test/command/suite/select/column/window_function/row_number/descending.expected
    test/command/suite/select/column/window_function/row_number/descending.test
  Copied files:
    lib/grn_window_function.h
      (from include/groonga.h)
    lib/grn_window_functions.h
      (from include/groonga.h)
  Modified files:
    include/groonga.h
    include/groonga/Makefile.am
    include/groonga/groonga.h
    include/groonga/obj.h
    lib/db.c
    lib/expr.c
    lib/grn_db.h
    lib/mrb/mrb_ctx.c
    lib/mrb/mrb_error.c
    lib/mrb/scripts/context/rc.rb
    lib/obj.c
    lib/proc/proc_select.c
    lib/sources.am
    lib/util.c
    test/unit/core/test-object.c

  Modified: include/groonga.h (+1 -0)
===================================================================
--- include/groonga.h    2016-05-18 00:16:33 +0900 (c24eff6)
+++ include/groonga.h    2016-05-18 00:22:11 +0900 (b219bdc)
@@ -42,5 +42,6 @@
 #include "groonga/time.h"
 #include "groonga/type.h"
 #include "groonga/util.h"
+#include "groonga/window_function.h"
 #include "groonga/windows.h"
 #include "groonga/windows_event_logger.h"

  Modified: include/groonga/Makefile.am (+1 -0)
===================================================================
--- include/groonga/Makefile.am    2016-05-18 00:16:33 +0900 (f9ac5ff)
+++ include/groonga/Makefile.am    2016-05-18 00:22:11 +0900 (e753a01)
@@ -31,5 +31,6 @@ groonga_include_HEADERS =			\
 	nfkc.h					\
 	normalizer.h				\
 	util.h					\
+	window_function.h			\
 	windows.h				\
 	windows_event_logger.h

  Modified: include/groonga/groonga.h (+4 -2)
===================================================================
--- include/groonga/groonga.h    2016-05-18 00:16:33 +0900 (4ea4b63)
+++ include/groonga/groonga.h    2016-05-18 00:22:11 +0900 (fdd15fe)
@@ -125,7 +125,8 @@ typedef enum {
   GRN_COMMAND_ERROR = -74,
   GRN_PLUGIN_ERROR = -75,
   GRN_SCORER_ERROR = -76,
-  GRN_CANCEL = -77
+  GRN_CANCEL = -77,
+  GRN_WINDOW_FUNCTION_ERROR = -78
 } grn_rc;
 
 GRN_API grn_rc grn_init(void);
@@ -507,7 +508,8 @@ typedef enum {
   GRN_PROC_HOOK,
   GRN_PROC_NORMALIZER,
   GRN_PROC_TOKEN_FILTER,
-  GRN_PROC_SCORER
+  GRN_PROC_SCORER,
+  GRN_PROC_WINDOW_FUNCTION
 } grn_proc_type;
 
 GRN_API grn_obj *grn_proc_create(grn_ctx *ctx,

  Modified: include/groonga/obj.h (+1 -0)
===================================================================
--- include/groonga/obj.h    2016-05-18 00:16:33 +0900 (4539467)
+++ include/groonga/obj.h    2016-05-18 00:22:11 +0900 (e811d87)
@@ -46,6 +46,7 @@ GRN_API grn_bool grn_obj_is_selector_only_proc(grn_ctx *ctx, grn_obj *obj);
 GRN_API grn_bool grn_obj_is_normalizer_proc(grn_ctx *ctx, grn_obj *obj);
 GRN_API grn_bool grn_obj_is_token_filter_proc(grn_ctx *ctx, grn_obj *obj);
 GRN_API grn_bool grn_obj_is_scorer_proc(grn_ctx *ctx, grn_obj *obj);
+GRN_API grn_bool grn_obj_is_window_function_proc(grn_ctx *ctx, grn_obj *obj);
 
 GRN_API grn_rc grn_obj_cast(grn_ctx *ctx,
                             grn_obj *src,

  Added: include/groonga/window_function.h (+67 -0) 100644
===================================================================
--- /dev/null
+++ include/groonga/window_function.h    2016-05-18 00:22:11 +0900 (df9be41)
@@ -0,0 +1,67 @@
+/*
+  Copyright(C) 2016 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#pragma once
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  GRN_WINDOW_DIRECTION_ASCENDING,
+  GRN_WINDOW_DIRECTION_DESCENDING
+} grn_window_direction;
+
+typedef struct _grn_window grn_window;
+
+GRN_API grn_id grn_window_next(grn_ctx *ctx,
+                               grn_window *window);
+GRN_API grn_rc grn_window_rewind(grn_ctx *ctx,
+                                 grn_window *window);
+GRN_API grn_rc grn_window_set_direction(grn_ctx *ctx,
+                                        grn_window *window,
+                                        grn_window_direction direction);
+GRN_API grn_obj *grn_window_get_table(grn_ctx *ctx,
+                                      grn_window *window);
+
+typedef struct _grn_window_definition {
+  grn_table_sort_key *sort_keys;
+  size_t n_sort_keys;
+} grn_window_definition;
+
+typedef grn_rc grn_window_function_func(grn_ctx *ctx,
+                                        grn_obj *output_column,
+                                        grn_window *window,
+                                        grn_obj *args,
+                                        int n_args);
+
+GRN_API grn_obj *grn_window_function_create(grn_ctx *ctx,
+                                            const char *name,
+                                            int name_size,
+                                            grn_window_function_func func);
+
+
+GRN_API grn_rc grn_table_apply_window_function(grn_ctx *ctx,
+                                               grn_obj *table,
+                                               grn_obj *output_column,
+                                               grn_window_definition *definition,
+                                               grn_obj *window_function_call);
+
+#ifdef __cplusplus
+}
+#endif

  Modified: lib/db.c (+3 -0)
===================================================================
--- lib/db.c    2016-05-18 00:16:33 +0900 (3781ebf)
+++ lib/db.c    2016-05-18 00:22:11 +0900 (b09cc83)
@@ -36,6 +36,7 @@
 #include "grn_report.h"
 #include "grn_util.h"
 #include "grn_cache.h"
+#include "grn_window_functions.h"
 #include <string.h>
 
 typedef struct {
@@ -421,6 +422,7 @@ grn_db_open(grn_ctx *ctx, const char *path)
     grn_db_init_builtin_normalizers(ctx);
     grn_db_init_builtin_scorers(ctx);
     grn_db_init_builtin_commands(ctx);
+    grn_db_init_builtin_window_functions(ctx);
 
     if (grn_table_size(ctx, (grn_obj *)s) > n_records) {
       grn_obj_flush(ctx, (grn_obj *)s);
@@ -11847,6 +11849,7 @@ grn_db_init_builtin_types(grn_ctx *ctx)
     grn_obj_register(ctx, db, buf, 5);
   }
   grn_db_init_builtin_commands(ctx);
+  grn_db_init_builtin_window_functions(ctx);
   for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_N_RESERVED_TYPES; id++) {
     grn_itoh(id, buf + 3, 2);
     grn_obj_register(ctx, db, buf, 5);

  Modified: lib/expr.c (+2 -1)
===================================================================
--- lib/expr.c    2016-05-18 00:16:33 +0900 (e1c82dc)
+++ lib/expr.c    2016-05-18 00:22:11 +0900 (4e78d9c)
@@ -885,7 +885,8 @@ grn_expr_append_obj(grn_ctx *ctx, grn_obj *expr, grn_obj *obj, grn_operator op,
           goto exit;
         }
         if (!(grn_obj_is_function_proc(ctx, proc) ||
-              grn_obj_is_scorer_proc(ctx, proc))) {
+              grn_obj_is_scorer_proc(ctx, proc) ||
+              grn_obj_is_window_function_proc(ctx, proc))) {
           grn_obj buffer;
 
           GRN_TEXT_INIT(&buffer, 0);

  Modified: lib/grn_db.h (+1 -0)
===================================================================
--- lib/grn_db.h    2016-05-18 00:16:33 +0900 (18fe337)
+++ lib/grn_db.h    2016-05-18 00:22:11 +0900 (6748e37)
@@ -185,6 +185,7 @@ struct _grn_proc {
     struct {
       grn_scorer_score_func *score;
     } scorer;
+    grn_window_function_func *window_function;
   } callbacks;
 
   void *user_data;

  Copied: lib/grn_window_function.h (+20 -26) 50%
===================================================================
--- include/groonga.h    2016-05-18 00:16:33 +0900 (c24eff6)
+++ lib/grn_window_function.h    2016-05-18 00:22:11 +0900 (71c71e9)
@@ -1,5 +1,5 @@
 /*
-  Copyright(C) 2014-2016 Brazil
+  Copyright(C) 2016 Brazil
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -18,29 +18,23 @@
 
 #pragma once
 
-#include "groonga/portability.h"
-#include "groonga/groonga.h"
+struct _grn_window {
+  grn_obj *table;
+  grn_obj ids;
+  size_t n_ids;
+  ssize_t current_index;
+  grn_window_direction direction;
+  grn_table_sort_key *sort_keys;
+  size_t n_sort_keys;
+};
 
-#include "groonga/array.h"
-#include "groonga/config.h"
-#include "groonga/dat.h"
-#include "groonga/db.h"
-#include "groonga/dump.h"
-#include "groonga/expr.h"
-#include "groonga/file_reader.h"
-#include "groonga/geo.h"
-#include "groonga/hash.h"
-#include "groonga/ii.h"
-#include "groonga/obj.h"
-#include "groonga/operator.h"
-#include "groonga/output.h"
-#include "groonga/pat.h"
-#include "groonga/request_canceler.h"
-#include "groonga/request_timer.h"
-#include "groonga/table.h"
-#include "groonga/thread.h"
-#include "groonga/time.h"
-#include "groonga/type.h"
-#include "groonga/util.h"
-#include "groonga/windows.h"
-#include "groonga/windows_event_logger.h"
+grn_rc grn_window_init(grn_ctx *ctx, grn_window *window, grn_obj *table);
+grn_rc grn_window_fin(grn_ctx *ctx, grn_window *window);
+grn_rc grn_window_set_sort_keys(grn_ctx *ctx,
+                                grn_window *window,
+                                grn_table_sort_key *sort_keys,
+                                size_t n_sort_keys);
+
+#ifdef __cplusplus
+}
+#endif

  Copied: lib/grn_window_functions.h (+6 -26) 50%
===================================================================
--- include/groonga.h    2016-05-18 00:16:33 +0900 (c24eff6)
+++ lib/grn_window_functions.h    2016-05-18 00:22:11 +0900 (a9d1e6f)
@@ -1,5 +1,5 @@
 /*
-  Copyright(C) 2014-2016 Brazil
+  Copyright(C) 2016 Brazil
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -18,29 +18,9 @@
 
 #pragma once
 
-#include "groonga/portability.h"
-#include "groonga/groonga.h"
+grn_rc grn_db_init_builtin_window_functions(grn_ctx *ctx);
 
-#include "groonga/array.h"
-#include "groonga/config.h"
-#include "groonga/dat.h"
-#include "groonga/db.h"
-#include "groonga/dump.h"
-#include "groonga/expr.h"
-#include "groonga/file_reader.h"
-#include "groonga/geo.h"
-#include "groonga/hash.h"
-#include "groonga/ii.h"
-#include "groonga/obj.h"
-#include "groonga/operator.h"
-#include "groonga/output.h"
-#include "groonga/pat.h"
-#include "groonga/request_canceler.h"
-#include "groonga/request_timer.h"
-#include "groonga/table.h"
-#include "groonga/thread.h"
-#include "groonga/time.h"
-#include "groonga/type.h"
-#include "groonga/util.h"
-#include "groonga/windows.h"
-#include "groonga/windows_event_logger.h"
+
+#ifdef __cplusplus
+}
+#endif

  Modified: lib/mrb/mrb_ctx.c (+6 -0)
===================================================================
--- lib/mrb/mrb_ctx.c    2016-05-18 00:16:33 +0900 (180ace2)
+++ lib/mrb/mrb_ctx.c    2016-05-18 00:22:11 +0900 (64be7e7)
@@ -735,6 +735,12 @@ grn_mrb_ctx_check(mrb_state *mrb)
                  "cancel: <%s>(%d)",
                  ctx->errbuf, ctx->rc);
     break;
+  case GRN_WINDOW_FUNCTION_ERROR:
+    error_class = mrb_class_get_under(mrb, module, "WindowFunctionError");
+    grn_snprintf(message, MESSAGE_SIZE, MESSAGE_SIZE,
+                 "window function error: <%s>(%d)",
+                 ctx->errbuf, ctx->rc);
+    break;
   }
 
   if (!error_class) {

  Modified: lib/mrb/mrb_error.c (+2 -0)
===================================================================
--- lib/mrb/mrb_error.c    2016-05-18 00:16:33 +0900 (910e642)
+++ lib/mrb/mrb_error.c    2016-05-18 00:22:11 +0900 (661910f)
@@ -194,5 +194,7 @@ grn_mrb_error_init(grn_ctx *ctx)
                          groonga_error_class);
   mrb_define_class_under(mrb, module, "Cancel",
                          groonga_error_class);
+  mrb_define_class_under(mrb, module, "WindowFunctionError",
+                         groonga_error_class);
 }
 #endif

  Modified: lib/mrb/scripts/context/rc.rb (+2 -0)
===================================================================
--- lib/mrb/scripts/context/rc.rb    2016-05-18 00:16:33 +0900 (8ed182e)
+++ lib/mrb/scripts/context/rc.rb    2016-05-18 00:22:11 +0900 (f2fecbe)
@@ -186,6 +186,8 @@ module Groonga
         register(:scorer_error, -76, ScorerError)
       CANCEL =
         register(:cancel, -77, Cancel)
+      WINDOW_FUNCTION_ERROR =
+        register(:window_function_error, -78, WindowFunctionError)
 
       GroongaError.rc = UNKNOWN_ERROR
     end

  Modified: lib/obj.c (+13 -0)
===================================================================
--- lib/obj.c    2016-05-18 00:16:33 +0900 (20ae871)
+++ lib/obj.c    2016-05-18 00:22:11 +0900 (0f1e1ce)
@@ -296,6 +296,19 @@ grn_obj_is_scorer_proc(grn_ctx *ctx, grn_obj *obj)
   return proc->type == GRN_PROC_SCORER;
 }
 
+grn_bool
+grn_obj_is_window_function_proc(grn_ctx *ctx, grn_obj *obj)
+{
+  grn_proc *proc;
+
+  if (!grn_obj_is_proc(ctx, obj)) {
+    return GRN_FALSE;
+  }
+
+  proc = (grn_proc *)obj;
+  return proc->type == GRN_PROC_WINDOW_FUNCTION;
+}
+
 static void
 grn_db_reindex(grn_ctx *ctx, grn_obj *db)
 {

  Modified: lib/proc/proc_select.c (+80 -92)
===================================================================
--- lib/proc/proc_select.c    2016-05-18 00:16:33 +0900 (e53d824)
+++ lib/proc/proc_select.c    2016-05-18 00:22:11 +0900 (2164670)
@@ -62,7 +62,9 @@ typedef struct {
   grn_obj *type;
   grn_obj_flags flags;
   grn_select_string value;
-  grn_select_string sortby;
+  struct {
+    grn_select_string sort_keys;
+  } window;
 } grn_column_data;
 
 typedef struct {
@@ -221,8 +223,8 @@ grn_column_data_init(grn_ctx *ctx,
   column->flags = GRN_OBJ_COLUMN_SCALAR;
   column->value.value = NULL;
   column->value.length = 0;
-  column->sortby.value = NULL;
-  column->sortby.length = 0;
+  column->window.sort_keys.value = NULL;
+  column->window.sort_keys.length = 0;
   return GRN_TRUE;
 }
 
@@ -232,7 +234,7 @@ grn_column_data_fill(grn_ctx *ctx,
                      grn_obj *type_raw,
                      grn_obj *flags,
                      grn_obj *value,
-                     grn_obj *sortby)
+                     grn_obj *window_sort_keys)
 {
   if (type_raw && GRN_TEXT_LEN(type_raw) > 0) {
     grn_obj *type;
@@ -289,7 +291,7 @@ grn_column_data_fill(grn_ctx *ctx,
   }
 
   GRN_SELECT_FILL_STRING(column->value, value);
-  GRN_SELECT_FILL_STRING(column->sortby, sortby);
+  GRN_SELECT_FILL_STRING(column->window.sort_keys, window_sort_keys);
 
   return GRN_TRUE;
 }
@@ -314,7 +316,9 @@ grn_column_data_collect(grn_ctx *ctx,
     grn_obj *type;
     grn_obj *flags;
     grn_obj *value;
-    grn_obj *sortby;
+    struct {
+      grn_obj *sort_keys;
+    } window;
 
     grn_hash_cursor_get_value(ctx, cursor, (void **)&column);
 
@@ -332,12 +336,12 @@ grn_column_data_collect(grn_ctx *ctx,
     GET_VAR(type);
     GET_VAR(flags);
     GET_VAR(value);
-    GET_VAR(sortby);
+    GET_VAR(window.sort_keys);
 
 #undef GET_VAR
 
     grn_column_data_fill(ctx, column,
-                         type, flags, value, sortby);
+                         type, flags, value, window.sort_keys);
   }
   grn_hash_cursor_close(ctx, cursor);
   return GRN_TRUE;
@@ -766,11 +770,6 @@ grn_select_apply_columns(grn_ctx *ctx,
   while (grn_hash_cursor_next(ctx, columns_cursor) != GRN_ID_NIL) {
     grn_column_data *column_data;
     grn_obj *column;
-    grn_obj *expression;
-    grn_obj *record;
-    grn_table_cursor *table_cursor;
-    grn_id id;
-    grn_obj *target_table = table;
 
     grn_hash_cursor_get_value(ctx, columns_cursor, (void **)&column_data);
 
@@ -792,50 +791,11 @@ grn_select_apply_columns(grn_ctx *ctx,
       break;
     }
 
-    if (column_data->sortby.length > 0) {
-      grn_table_sort_key *sort_keys;
-      uint32_t n_sort_keys;
-      sort_keys = grn_table_sort_key_from_str(ctx,
-                                              column_data->sortby.value,
-                                              column_data->sortby.length,
-                                              table, &n_sort_keys);
-      if (!sort_keys) {
-        grn_obj_close(ctx, column);
-        GRN_PLUGIN_ERROR(ctx,
-                         GRN_INVALID_ARGUMENT,
-                         "[select][column][%s][%.*s] failed to parse sort key: %s",
-                         grn_column_stage_name(column_data->stage),
-                         (int)(column_data->label.length),
-                         column_data->label.value,
-                         ctx->errbuf);
-        break;
-      }
-
-      target_table = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY,
-                                      NULL, table);
-      if (!target_table) {
-        grn_obj_close(ctx, column);
-        grn_table_sort_key_close(ctx, sort_keys, n_sort_keys);
-        GRN_PLUGIN_ERROR(ctx,
-                         GRN_INVALID_ARGUMENT,
-                         "[select][column][%s][%.*s] failed to create sorted table: %s",
-                         grn_column_stage_name(column_data->stage),
-                         (int)(column_data->label.length),
-                         column_data->label.value,
-                         ctx->errbuf);
-        break;
-      }
-      grn_table_sort(ctx, table, 0, -1,
-                     target_table, sort_keys, n_sort_keys);
-      grn_table_sort_key_close(ctx, sort_keys, n_sort_keys);
-    }
-
-    GRN_EXPR_CREATE_FOR_QUERY(ctx, target_table, expression, record);
+    grn_obj *expression;
+    grn_obj *record;
+    GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expression, record);
     if (!expression) {
       grn_obj_close(ctx, column);
-      if (column_data->sortby.length > 0) {
-        grn_obj_unlink(ctx, target_table);
-      }
       GRN_PLUGIN_ERROR(ctx,
                        GRN_INVALID_ARGUMENT,
                        "[select][column][%s][%.*s] "
@@ -857,9 +817,6 @@ grn_select_apply_columns(grn_ctx *ctx,
     if (ctx->rc != GRN_SUCCESS) {
       grn_obj_close(ctx, expression);
       grn_obj_close(ctx, column);
-      if (column_data->sortby.length > 0) {
-        grn_obj_unlink(ctx, target_table);
-      }
       GRN_PLUGIN_ERROR(ctx,
                        GRN_INVALID_ARGUMENT,
                        "[select][column][%s][%.*s] "
@@ -874,46 +831,77 @@ grn_select_apply_columns(grn_ctx *ctx,
     }
     grn_select_expression_set_condition(ctx, expression, condition);
 
-    table_cursor = grn_table_cursor_open(ctx, target_table,
-                                         NULL, 0,
-                                         NULL, 0,
-                                         0, -1, GRN_CURSOR_BY_ID);
-    if (!table_cursor) {
-      grn_obj_close(ctx, expression);
-      grn_obj_close(ctx, column);
-      if (column_data->sortby.length > 0) {
-        grn_obj_unlink(ctx, target_table);
+    if (column_data->window.sort_keys.length > 0) {
+      grn_window_definition definition;
+      int n_sort_keys;
+      grn_rc rc;
+
+      definition.sort_keys =
+        grn_table_sort_key_from_str(ctx,
+                                    column_data->window.sort_keys.value,
+                                    column_data->window.sort_keys.length,
+                                    table, &n_sort_keys);
+      definition.n_sort_keys = n_sort_keys;
+      if (!definition.sort_keys) {
+        grn_obj_close(ctx, expression);
+        grn_obj_close(ctx, column);
+        GRN_PLUGIN_ERROR(ctx,
+                         GRN_INVALID_ARGUMENT,
+                         "[select][column][%s][%.*s] "
+                         "failed to parse sort keys: %s",
+                         grn_column_stage_name(column_data->stage),
+                         (int)(column_data->label.length),
+                         column_data->label.value,
+                         ctx->errbuf);
+        break;
       }
-      GRN_PLUGIN_ERROR(ctx,
-                       GRN_INVALID_ARGUMENT,
-                       "[select][column][%s][%.*s] "
-                       "failed to create cursor for getting records: %s",
-                       grn_column_stage_name(column_data->stage),
-                       (int)(column_data->label.length),
-                       column_data->label.value,
-                       ctx->errbuf);
-      break;
-    }
 
-    while ((id = grn_table_cursor_next(ctx, table_cursor)) != GRN_ID_NIL) {
-      grn_obj *value;
-
-      GRN_RECORD_SET(ctx, record, id);
-      value = grn_expr_exec(ctx, expression, 0);
-      if (value) {
-        grn_id target_record_id = id;
-        if (column_data->sortby.length > 0) {
-          void *sorted_table_value;
-          grn_table_cursor_get_value(ctx, table_cursor, &sorted_table_value);
-          target_record_id = *((grn_id *)sorted_table_value);
+      rc = grn_table_apply_window_function(ctx,
+                                           table,
+                                           column,
+                                           &definition,
+                                           expression);
+      grn_table_sort_key_close(ctx,
+                               definition.sort_keys,
+                               definition.n_sort_keys);
+      if (rc != GRN_SUCCESS) {
+        grn_obj_close(ctx, expression);
+        grn_obj_close(ctx, column);
+        break;
+      }
+    } else {
+      grn_table_cursor *table_cursor;
+      grn_id id;
+
+      table_cursor = grn_table_cursor_open(ctx, table,
+                                           NULL, 0,
+                                           NULL, 0,
+                                           0, -1, GRN_CURSOR_BY_ID);
+      if (!table_cursor) {
+        grn_obj_close(ctx, expression);
+        grn_obj_close(ctx, column);
+        GRN_PLUGIN_ERROR(ctx,
+                         GRN_INVALID_ARGUMENT,
+                         "[select][column][%s][%.*s] "
+                         "failed to create cursor for getting records: %s",
+                         grn_column_stage_name(column_data->stage),
+                         (int)(column_data->label.length),
+                         column_data->label.value,
+                         ctx->errbuf);
+        break;
+      }
+
+      while ((id = grn_table_cursor_next(ctx, table_cursor)) != GRN_ID_NIL) {
+        grn_obj *value;
+
+        GRN_RECORD_SET(ctx, record, id);
+        value = grn_expr_exec(ctx, expression, 0);
+        if (value) {
+          grn_obj_set_value(ctx, column, id, value, GRN_OBJ_SET);
         }
-        grn_obj_set_value(ctx, column, target_record_id, value, GRN_OBJ_SET);
       }
     }
 
-    if (column_data->sortby.length > 0) {
-      grn_obj_unlink(ctx, target_table);
-    }
     grn_obj_close(ctx, expression);
   }
 

  Modified: lib/sources.am (+5 -1)
===================================================================
--- lib/sources.am    2016-05-18 00:16:33 +0900 (5c960d4)
+++ lib/sources.am    2016-05-18 00:22:11 +0900 (c0ef10b)
@@ -91,4 +91,8 @@ libgroonga_la_SOURCES =				\
 	grn_util.h				\
 	windows.c				\
 	windows_event_logger.c			\
-	file_reader.c
+	file_reader.c				\
+	window_function.c			\
+	grn_window_function.h			\
+	window_functions.c			\
+	grn_window_functions.h

  Modified: lib/util.c (+3 -0)
===================================================================
--- lib/util.c    2016-05-18 00:16:33 +0900 (ccfcc51)
+++ lib/util.c    2016-05-18 00:22:11 +0900 (9483474)
@@ -271,6 +271,9 @@ grn_proc_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
   case GRN_PROC_SCORER :
     GRN_TEXT_PUTS(ctx, buf, "scorer");
     break;
+  case GRN_PROC_WINDOW_FUNCTION :
+    GRN_TEXT_PUTS(ctx, buf, "window-function");
+    break;
   }
   GRN_TEXT_PUTS(ctx, buf, " ");
 

  Added: lib/window_function.c (+344 -0) 100644
===================================================================
--- /dev/null
+++ lib/window_function.c    2016-05-18 00:22:11 +0900 (41e04bd)
@@ -0,0 +1,344 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+  Copyright(C) 2016 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "grn_ctx.h"
+#include "grn_db.h"
+#include "grn_expr.h"
+#include "grn_window_function.h"
+
+#include <string.h>
+
+grn_rc
+grn_window_init(grn_ctx *ctx, grn_window *window, grn_obj *table)
+{
+  GRN_API_ENTER;
+
+  window->table = table;
+  GRN_RECORD_INIT(&(window->ids), GRN_OBJ_VECTOR, grn_obj_id(ctx, table));
+  window->n_ids = 0;
+  window->current_index = 0;
+  window->direction = GRN_WINDOW_DIRECTION_ASCENDING;
+  window->sort_keys = NULL;
+  window->n_sort_keys = 0;
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_rc
+grn_window_fin(grn_ctx *ctx, grn_window *window)
+{
+  GRN_API_ENTER;
+
+  GRN_OBJ_FIN(ctx, &(window->ids));
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_id
+grn_window_next(grn_ctx *ctx, grn_window *window)
+{
+  grn_id next_id;
+
+  GRN_API_ENTER;
+
+  if (!window) {
+    GRN_API_RETURN(GRN_ID_NIL);
+  }
+
+  if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) {
+    if (window->current_index >= window->n_ids) {
+      GRN_API_RETURN(GRN_ID_NIL);
+    }
+  } else {
+    if (window->current_index < 0) {
+      GRN_API_RETURN(GRN_ID_NIL);
+    }
+  }
+
+  next_id = GRN_RECORD_VALUE_AT(&(window->ids), window->current_index);
+  if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) {
+    window->current_index++;
+  } else {
+    window->current_index--;
+  }
+
+  GRN_API_RETURN(next_id);
+}
+
+grn_rc
+grn_window_rewind(grn_ctx *ctx, grn_window *window)
+{
+  GRN_API_ENTER;
+
+  if (!window) {
+    ERR(GRN_INVALID_ARGUMENT, "[window][rewind] window is NULL");
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) {
+    window->current_index = 0;
+  } else {
+    window->current_index = window->n_ids - 1;
+  }
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_obj *
+grn_window_get_table(grn_ctx *ctx, grn_window *window)
+{
+  GRN_API_ENTER;
+
+  if (!window) {
+    ERR(GRN_INVALID_ARGUMENT, "[window][rewind] window is NULL");
+    GRN_API_RETURN(NULL);
+  }
+
+  GRN_API_RETURN(window->table);
+}
+
+grn_rc
+grn_window_set_direction(grn_ctx *ctx,
+                         grn_window *window,
+                         grn_window_direction direction)
+{
+  GRN_API_ENTER;
+
+  if (!window) {
+    ERR(GRN_INVALID_ARGUMENT, "[window][set][direction] window is NULL");
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  switch (direction) {
+  case GRN_WINDOW_DIRECTION_ASCENDING :
+    window->direction = direction;
+    window->current_index = 0;
+    break;
+  case GRN_WINDOW_DIRECTION_DESCENDING :
+    window->direction = direction;
+    window->current_index = window->n_ids - 1;
+    break;
+  default :
+    ERR(GRN_INVALID_ARGUMENT,
+        "[window][set][direction] direction must be "
+        "GRN_WINDOW_DIRECTION_ASCENDING(%d) or "
+        "GRN_WINDOW_DIRECTION_DESCENDING(%d): %d",
+        GRN_WINDOW_DIRECTION_ASCENDING,
+        GRN_WINDOW_DIRECTION_DESCENDING,
+        direction);
+    GRN_API_RETURN(ctx->rc);
+    break;
+  }
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_rc
+grn_window_set_sort_keys(grn_ctx *ctx,
+                         grn_window *window,
+                         grn_table_sort_key *sort_keys,
+                         size_t n_sort_keys)
+{
+  grn_obj *sorted;
+
+  GRN_API_ENTER;
+
+  if (!window) {
+    ERR(GRN_INVALID_ARGUMENT, "[window][set][sort-keys] window is NULL");
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  sorted = grn_table_create(ctx,
+                            NULL, 0, NULL,
+                            GRN_OBJ_TABLE_NO_KEY,
+                            NULL, window->table);
+  if (!sorted) {
+    ERR(ctx->rc,
+        "[window][set][sort-keys] "
+        "failed to create a table to store sorted records: %s",
+        ctx->errbuf);
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  grn_table_sort(ctx, window->table, 0, -1, sorted, sort_keys, n_sort_keys);
+  if (ctx->rc != GRN_SUCCESS) {
+    ERR(ctx->rc,
+        "[window][set][sort-keys] "
+        "failed to sort: %s",
+        ctx->errbuf);
+    grn_obj_unlink(ctx, sorted);
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  GRN_BULK_REWIND(&(window->ids));
+  GRN_TABLE_EACH_BEGIN(ctx, sorted, cursor, id) {
+    void *value;
+    grn_id record_id;
+
+    grn_table_cursor_get_value(ctx, cursor, &value);
+    record_id = *((grn_id *)value);
+    GRN_RECORD_PUT(ctx, &(window->ids), record_id);
+  } GRN_TABLE_EACH_END(ctx, cursor);
+
+  grn_obj_unlink(ctx, sorted);
+
+  window->n_ids = GRN_BULK_VSIZE(&(window->ids)) / sizeof(grn_id);
+
+  grn_window_set_direction(ctx, window, window->direction);
+
+  window->sort_keys = sort_keys;
+  window->n_sort_keys = n_sort_keys;
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_obj *
+grn_window_function_create(grn_ctx *ctx,
+                           const char *name,
+                           int name_size,
+                           grn_window_function_func func)
+{
+  grn_obj *window_function = NULL;
+
+  GRN_API_ENTER;
+
+  if (name_size == -1) {
+    name_size = strlen(name);
+  }
+
+  window_function = grn_proc_create(ctx,
+                                    name,
+                                    name_size,
+                                    GRN_PROC_WINDOW_FUNCTION,
+                                    NULL, NULL, NULL, 0, NULL);
+  if (!window_function) {
+    ERR(GRN_WINDOW_FUNCTION_ERROR,
+        "[window-function][%.*s] failed to create proc: %s",
+        name_size, name,
+        ctx->errbuf);
+    GRN_API_RETURN(NULL);
+  }
+
+  {
+    grn_proc *proc = (grn_proc *)window_function;
+    proc->callbacks.window_function = func;
+  }
+
+  GRN_API_RETURN(window_function);
+}
+
+static grn_bool
+grn_expr_is_window_function_call(grn_ctx *ctx,
+                                 grn_obj *window_function_call)
+{
+  grn_expr *expr = (grn_expr *)window_function_call;
+  grn_expr_code *func;
+  grn_expr_code *call;
+
+  func = &(expr->codes[0]);
+  call = &(expr->codes[expr->codes_curr - 1]);
+
+  if (func->op != GRN_OP_PUSH) {
+    return GRN_FALSE;
+  }
+  if (!grn_obj_is_window_function_proc(ctx, func->value)) {
+    return GRN_FALSE;
+  }
+
+  if (call->op != GRN_OP_CALL) {
+    return GRN_FALSE;
+  }
+  if (call->nargs != (expr->codes_curr - 2)) {
+    return GRN_FALSE;
+  }
+
+  return GRN_TRUE;
+}
+
+static grn_rc
+grn_expr_call_window_function(grn_ctx *ctx,
+                              grn_obj *output_column,
+                              grn_window *window,
+                              grn_obj *window_function_call)
+{
+  grn_rc rc;
+  grn_expr *expr = (grn_expr *)window_function_call;
+  grn_proc *proc;
+  int32_t i, n;
+  grn_obj args;
+
+  proc = (grn_proc *)(expr->codes[0].value);
+
+  GRN_PTR_INIT(&args, GRN_OBJ_VECTOR, GRN_ID_NIL);
+  n = expr->codes_curr - 2;
+  for (i = 1; i < n; i++) {
+    /* TODO: Check op. */
+    GRN_PTR_PUT(ctx, &args, expr->codes[i].value);
+  }
+  rc = proc->callbacks.window_function(ctx,
+                                       output_column,
+                                       window,
+                                       (grn_obj *)GRN_BULK_HEAD(&args),
+                                       GRN_BULK_VSIZE(&args) / sizeof(grn_obj *));
+  GRN_OBJ_FIN(ctx, &args);
+
+  return rc;
+}
+
+grn_rc
+grn_table_apply_window_function(grn_ctx *ctx,
+                                grn_obj *table,
+                                grn_obj *output_column,
+                                grn_window_definition *definition,
+                                grn_obj *window_function_call)
+{
+  grn_window window;
+
+  GRN_API_ENTER;
+
+  if (!table) {
+    ERR(GRN_INVALID_ARGUMENT,
+        "[table][apply][window-function] table is NULL");
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  if (!grn_expr_is_window_function_call(ctx, window_function_call)) {
+    grn_obj inspected;
+    GRN_TEXT_INIT(&inspected, 0);
+    grn_inspect(ctx, window_function_call, &inspected);
+    ERR(GRN_INVALID_ARGUMENT,
+        "[table][apply][window-function] must be window function call: %.*s",
+        (int)GRN_TEXT_LEN(&inspected),
+        GRN_TEXT_VALUE(&inspected));
+    GRN_OBJ_FIN(ctx, &inspected);
+    GRN_API_RETURN(ctx->rc);
+  }
+
+  grn_window_init(ctx, &window, table);
+  grn_window_set_direction(ctx, &window, window.direction);
+  grn_window_set_sort_keys(ctx, &window,
+                           definition->sort_keys,
+                           definition->n_sort_keys);
+  grn_expr_call_window_function(ctx,
+                                output_column,
+                                &window,
+                                window_function_call);
+  grn_window_fin(ctx, &window);
+
+  GRN_API_RETURN(GRN_SUCCESS);
+}

  Added: lib/window_functions.c (+51 -0) 100644
===================================================================
--- /dev/null
+++ lib/window_functions.c    2016-05-18 00:22:11 +0900 (6ef478a)
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+  Copyright(C) 2016 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "grn_db.h"
+#include "grn_window_functions.h"
+
+static grn_rc
+window_function_row_number(grn_ctx *ctx,
+                           grn_obj *output_column,
+                           grn_window *window,
+                           grn_obj *args,
+                           int n_args)
+{
+  grn_id id;
+  uint32_t i = 1;
+  grn_obj value;
+
+  GRN_UINT32_INIT(&value, 0);
+  while ((id = grn_window_next(ctx, window))) {
+    GRN_UINT32_SET(ctx, &value, i);
+    grn_obj_set_value(ctx, output_column, id, &value, GRN_OBJ_SET);
+    i++;
+  }
+  GRN_OBJ_FIN(ctx, &value);
+
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_db_init_builtin_window_functions(grn_ctx *ctx)
+{
+  grn_window_function_create(ctx,
+                             "row_number", -1,
+                             window_function_row_number);
+  return GRN_SUCCESS;
+}

  Added: test/command/suite/select/column/window_function/row_number/ascending.expected (+73 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/column/window_function/row_number/ascending.expected    2016-05-18 00:22:11 +0900 (363631d)
@@ -0,0 +1,73 @@
+table_create Items TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+column_create Items price COLUMN_SCALAR UInt32
+[[0,0.0,0.0],true]
+load --table Items
+[
+{"_key": "item1", "price": 666},
+{"_key": "item2", "price": 999},
+{"_key": "item3", "price": 777},
+{"_key": "item4", "price": 111},
+{"_key": "item5", "price": 333},
+{"_key": "item6", "price": 222}
+]
+[[0,0.0,0.0],6]
+select Items   --column[nth_row].stage initial   --column[nth_row].value 'row_number()'   --column[nth_row].type UInt32   --column[nth_row].window.sort_keys price   --output_columns '_key, price, nth_row'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        6
+      ],
+      [
+        [
+          "_key",
+          "ShortText"
+        ],
+        [
+          "price",
+          "UInt32"
+        ],
+        [
+          "nth_row",
+          "UInt32"
+        ]
+      ],
+      [
+        "item1",
+        666,
+        4
+      ],
+      [
+        "item2",
+        999,
+        6
+      ],
+      [
+        "item3",
+        777,
+        5
+      ],
+      [
+        "item4",
+        111,
+        1
+      ],
+      [
+        "item5",
+        333,
+        3
+      ],
+      [
+        "item6",
+        222,
+        2
+      ]
+    ]
+  ]
+]

  Added: test/command/suite/select/column/window_function/row_number/ascending.test (+19 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/column/window_function/row_number/ascending.test    2016-05-18 00:22:11 +0900 (98075ba)
@@ -0,0 +1,19 @@
+table_create Items TABLE_HASH_KEY ShortText
+column_create Items price COLUMN_SCALAR UInt32
+
+load --table Items
+[
+{"_key": "item1", "price": 666},
+{"_key": "item2", "price": 999},
+{"_key": "item3", "price": 777},
+{"_key": "item4", "price": 111},
+{"_key": "item5", "price": 333},
+{"_key": "item6", "price": 222}
+]
+
+select Items \
+  --column[nth_row].stage initial \
+  --column[nth_row].value 'row_number()' \
+  --column[nth_row].type UInt32 \
+  --column[nth_row].window.sort_keys price \
+  --output_columns '_key, price, nth_row'

  Added: test/command/suite/select/column/window_function/row_number/descending.expected (+73 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/column/window_function/row_number/descending.expected    2016-05-18 00:22:11 +0900 (f7209e5)
@@ -0,0 +1,73 @@
+table_create Items TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+column_create Items price COLUMN_SCALAR UInt32
+[[0,0.0,0.0],true]
+load --table Items
+[
+{"_key": "item1", "price": 666},
+{"_key": "item2", "price": 999},
+{"_key": "item3", "price": 777},
+{"_key": "item4", "price": 111},
+{"_key": "item5", "price": 333},
+{"_key": "item6", "price": 222}
+]
+[[0,0.0,0.0],6]
+select Items   --column[nth_row].stage initial   --column[nth_row].value 'row_number()'   --column[nth_row].type UInt32   --column[nth_row].window.sort_keys -price   --output_columns '_key, price, nth_row'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        6
+      ],
+      [
+        [
+          "_key",
+          "ShortText"
+        ],
+        [
+          "price",
+          "UInt32"
+        ],
+        [
+          "nth_row",
+          "UInt32"
+        ]
+      ],
+      [
+        "item1",
+        666,
+        3
+      ],
+      [
+        "item2",
+        999,
+        1
+      ],
+      [
+        "item3",
+        777,
+        2
+      ],
+      [
+        "item4",
+        111,
+        6
+      ],
+      [
+        "item5",
+        333,
+        4
+      ],
+      [
+        "item6",
+        222,
+        5
+      ]
+    ]
+  ]
+]

  Added: test/command/suite/select/column/window_function/row_number/descending.test (+19 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/column/window_function/row_number/descending.test    2016-05-18 00:22:11 +0900 (071702f)
@@ -0,0 +1,19 @@
+table_create Items TABLE_HASH_KEY ShortText
+column_create Items price COLUMN_SCALAR UInt32
+
+load --table Items
+[
+{"_key": "item1", "price": 666},
+{"_key": "item2", "price": 999},
+{"_key": "item3", "price": 777},
+{"_key": "item4", "price": 111},
+{"_key": "item5", "price": 333},
+{"_key": "item6", "price": 222}
+]
+
+select Items \
+  --column[nth_row].stage initial \
+  --column[nth_row].value 'row_number()' \
+  --column[nth_row].type UInt32 \
+  --column[nth_row].window.sort_keys -price \
+  --output_columns '_key, price, nth_row'

  Modified: test/unit/core/test-object.c (+34 -0)
===================================================================
--- test/unit/core/test-object.c    2016-05-18 00:16:33 +0900 (159b269)
+++ test/unit/core/test-object.c    2016-05-18 00:22:11 +0900 (b716b9e)
@@ -55,6 +55,8 @@ void data_is_token_filter_proc(void);
 void test_is_token_filter_proc(gconstpointer data);
 void data_is_scorer_proc(void);
 void test_is_scorer_proc(gconstpointer data);
+void data_is_window_function_proc(void);
+void test_is_window_function_proc(gconstpointer data);
 void data_type_to_string(void);
 void test_type_to_string(gconstpointer data);
 
@@ -628,6 +630,38 @@ test_is_scorer_proc(gconstpointer data)
 }
 
 void
+data_is_window_function_proc(void)
+{
+#define ADD_DATUM(expected, name)                                       \
+  gcut_add_datum((expected ?                                            \
+                  "window-function-proc - " name :                      \
+                  "not window-function-proc - " name),                  \
+                 "expected", G_TYPE_BOOLEAN, expected,                  \
+                 "name", G_TYPE_STRING, name,                           \
+                 NULL)
+
+  ADD_DATUM(TRUE, "row_number");
+  ADD_DATUM(FALSE, "geo_in_circle");
+
+#undef ADD_DATUM
+}
+
+void
+test_is_window_function_proc(gconstpointer data)
+{
+  const gchar *name;
+  grn_obj *object;
+
+  name = gcut_data_get_string(data, "name");
+  object = grn_ctx_get(context, name, strlen(name));
+  if (gcut_data_get_string(data, "expected")) {
+    cut_assert_true(grn_obj_is_window_function_proc(context, object));
+  } else {
+    cut_assert_false(grn_obj_is_window_function_proc(context, object));
+  }
+}
+
+void
 data_type_to_string(void)
 {
 #define ADD_DATUM(expected, type)                                      \
-------------- next part --------------
HTML����������������������������...
Download 



More information about the Groonga-commit mailing list
Back to archive index