groonga 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/NEWS.ja.rdoc +18 -3
  2. data/NEWS.rdoc +18 -3
  3. data/README.ja.rdoc +2 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +14 -5
  6. data/TUTORIAL.ja.rdoc +82 -16
  7. data/benchmark/{read-write-small-many-items.rb → read-write-many-small-items.rb} +26 -23
  8. data/benchmark/{write-small-many-items.rb → write-many-small-items.rb} +26 -23
  9. data/example/bookmark.rb +49 -5
  10. data/ext/rb-grn-array.c +11 -1
  11. data/ext/rb-grn-column.c +132 -5
  12. data/ext/rb-grn-context.c +85 -80
  13. data/ext/rb-grn-database.c +182 -9
  14. data/ext/rb-grn-expression-builder.c +69 -0
  15. data/ext/rb-grn-expression.c +314 -0
  16. data/ext/rb-grn-fix-size-column.c +68 -89
  17. data/ext/rb-grn-hash.c +14 -5
  18. data/ext/rb-grn-index-column.c +14 -55
  19. data/ext/rb-grn-object.c +206 -75
  20. data/ext/rb-grn-operation.c +92 -0
  21. data/ext/rb-grn-patricia-trie.c +10 -32
  22. data/ext/rb-grn-query.c +9 -9
  23. data/ext/rb-grn-table-cursor.c +19 -80
  24. data/ext/rb-grn-table-key-support.c +33 -39
  25. data/ext/rb-grn-table.c +436 -79
  26. data/ext/rb-grn-type.c +10 -3
  27. data/ext/rb-grn-utils.c +131 -4
  28. data/ext/rb-grn-variable-size-column.c +36 -0
  29. data/ext/rb-grn-variable.c +90 -0
  30. data/ext/rb-grn.h +109 -56
  31. data/ext/rb-groonga.c +4 -0
  32. data/extconf.rb +39 -13
  33. data/html/index.html +2 -2
  34. data/lib/groonga.rb +22 -0
  35. data/lib/groonga/expression-builder.rb +141 -0
  36. data/lib/groonga/record.rb +25 -1
  37. data/lib/groonga/schema.rb +418 -0
  38. data/test/test-column.rb +11 -23
  39. data/test/test-context.rb +1 -1
  40. data/test/test-database.rb +60 -19
  41. data/test/test-expression-builder.rb +114 -0
  42. data/test/test-expression.rb +55 -0
  43. data/test/test-fix-size-column.rb +53 -0
  44. data/test/test-hash.rb +10 -3
  45. data/test/test-index-column.rb +24 -0
  46. data/test/test-patricia-trie.rb +9 -0
  47. data/test/test-procedure.rb +5 -5
  48. data/test/test-record.rb +71 -4
  49. data/test/test-schema.rb +207 -0
  50. data/test/test-table.rb +94 -12
  51. data/test/test-type.rb +18 -11
  52. data/test/test-variable-size-column.rb +53 -0
  53. data/test/test-variable.rb +28 -0
  54. metadata +18 -5
data/ext/rb-grn-array.c CHANGED
@@ -65,6 +65,11 @@ VALUE rb_cGrnArray;
65
65
  * [+:value_size+]
66
66
  * 値の大きさを指定する。省略すると0になる。
67
67
  *
68
+ * [+:sub_records+]
69
+ * +true+を指定すると#groupでグループ化したときに、
70
+ * <tt>record[".:nsubrecs"]</tt>でグループに含まれるレコー
71
+ * ドの件数を取得できる。
72
+ *
68
73
  * 使用例:
69
74
  *
70
75
  * 無名一時テーブルを生成する。
@@ -92,7 +97,7 @@ rb_grn_array_s_create (int argc, VALUE *argv, VALUE klass)
92
97
  grn_obj_flags flags = GRN_TABLE_NO_KEY;
93
98
  VALUE rb_table;
94
99
  VALUE options, rb_context, rb_name, rb_path, rb_persistent;
95
- VALUE rb_value_size;
100
+ VALUE rb_value_size, rb_sub_records;
96
101
 
97
102
  rb_scan_args(argc, argv, "01", &options);
98
103
 
@@ -102,6 +107,7 @@ rb_grn_array_s_create (int argc, VALUE *argv, VALUE klass)
102
107
  "path", &rb_path,
103
108
  "persistent", &rb_persistent,
104
109
  "value_size", &rb_value_size,
110
+ "sub_records", &rb_sub_records,
105
111
  NULL);
106
112
 
107
113
  context = rb_grn_context_ensure(&rb_context);
@@ -109,6 +115,7 @@ rb_grn_array_s_create (int argc, VALUE *argv, VALUE klass)
109
115
  if (!NIL_P(rb_name)) {
110
116
  name = StringValuePtr(rb_name);
111
117
  name_size = RSTRING_LEN(rb_name);
118
+ flags |= GRN_OBJ_PERSISTENT;
112
119
  }
113
120
 
114
121
  if (!NIL_P(rb_path)) {
@@ -122,6 +129,9 @@ rb_grn_array_s_create (int argc, VALUE *argv, VALUE klass)
122
129
  if (!NIL_P(rb_value_size))
123
130
  value_size = NUM2UINT(rb_value_size);
124
131
 
132
+ if (RVAL2CBOOL(rb_sub_records))
133
+ flags |= GRN_OBJ_WITH_SUBREC;
134
+
125
135
  table = grn_table_create(context, name, name_size, path,
126
136
  flags, NULL, value_size);
127
137
  if (!table)
data/ext/rb-grn-column.c CHANGED
@@ -21,7 +21,6 @@
21
21
  #define SELF(object) ((RbGrnColumn *)DATA_PTR(object))
22
22
 
23
23
  VALUE rb_cGrnColumn;
24
- VALUE rb_cGrnVarSizeColumn;
25
24
 
26
25
  /*
27
26
  * Document-class: Groonga::Column < Groonga::Object
@@ -33,7 +32,7 @@ VALUE rb_cGrnVarSizeColumn;
33
32
  * カラムには大きく分けて3種類ある。
34
33
  * [Groonga::FixSizeColumn]
35
34
  * 固定長のデータを格納するカラム。
36
- * [Groonga::VarSizeColumn]
35
+ * [Groonga::VariableSizeColumn]
37
36
  * 可変長のデータを格納するカラム。
38
37
  * [Groonga::IndexColumn]
39
38
  * 転置インデックスを格納するカラム。全文検索や参照元レコー
@@ -65,6 +64,49 @@ rb_grn_column_to_ruby_object (VALUE klass, grn_ctx *context, grn_obj *column,
65
64
  return GRNOBJECT2RVAL(klass, context, column, owner);
66
65
  }
67
66
 
67
+ void
68
+ rb_grn_column_bind (RbGrnColumn *rb_column,
69
+ grn_ctx *context, grn_obj *column)
70
+ {
71
+ RbGrnObject *rb_grn_object;
72
+
73
+ rb_grn_object = RB_GRN_OBJECT(rb_column);
74
+
75
+ rb_column->value = grn_obj_open(context, GRN_BULK, 0,
76
+ rb_grn_object->range_id);
77
+ }
78
+
79
+ void
80
+ rb_grn_column_finalizer (grn_ctx *context, grn_obj *grn_object,
81
+ RbGrnColumn *rb_column)
82
+ {
83
+ if (context && rb_column->value)
84
+ grn_obj_close(context, rb_column->value);
85
+ rb_column->value = NULL;
86
+ }
87
+
88
+ void
89
+ rb_grn_column_deconstruct (RbGrnColumn *rb_column,
90
+ grn_obj **column,
91
+ grn_ctx **context,
92
+ grn_id *domain_id,
93
+ grn_obj **domain,
94
+ grn_obj **value,
95
+ grn_id *range_id,
96
+ grn_obj **range)
97
+ {
98
+ RbGrnObject *rb_grn_object;
99
+
100
+ rb_grn_object = RB_GRN_OBJECT(rb_column);
101
+ rb_grn_object_deconstruct(rb_grn_object, column, context,
102
+ domain_id, domain,
103
+ range_id, range);
104
+
105
+ if (value)
106
+ *value = rb_column->value;
107
+ }
108
+
109
+
68
110
  /*
69
111
  * call-seq:
70
112
  * column.table -> Groonga::Table
@@ -87,16 +129,101 @@ rb_grn_column_get_table (VALUE self)
87
129
  return GRNOBJECT2RVAL(Qnil, context, table, RB_GRN_FALSE);
88
130
  }
89
131
 
132
+ /*
133
+ * call-seq:
134
+ * column.local_name
135
+ *
136
+ * テーブル名を除いたカラム名を返す。
137
+ *
138
+ * items = Groonga::Array.create(:name => "<items>")
139
+ * title = items.define_column("title", "<shorttext>")
140
+ * title.name # => "<items>.title"
141
+ * title.local_name # => "title"
142
+ */
143
+ static VALUE
144
+ rb_grn_column_get_local_name (VALUE self)
145
+ {
146
+ grn_ctx *context = NULL;
147
+ grn_obj *column;
148
+ int name_size;
149
+ VALUE rb_name;
150
+
151
+ rb_grn_object_deconstruct((RbGrnObject *)(SELF(self)), &column, &context,
152
+ NULL, NULL,
153
+ NULL, NULL);
154
+ name_size = grn_column_name(context, column, NULL, 0);
155
+ if (name_size == 0)
156
+ return Qnil;
157
+
158
+ rb_name = rb_str_buf_new(name_size);
159
+ rb_str_set_len(rb_name, name_size);
160
+ grn_column_name(context, column, RSTRING_PTR(rb_name), name_size);
161
+ return rb_name;
162
+ }
163
+
164
+ static VALUE
165
+ rb_grn_column_select (int argc, VALUE *argv, VALUE self)
166
+ {
167
+ grn_ctx *context;
168
+ grn_obj *table, *column, *result, *expression;
169
+ grn_operator operator = GRN_OP_OR;
170
+ grn_rc rc;
171
+ VALUE options;
172
+ VALUE rb_query, rb_name, rb_operator, rb_result;
173
+ VALUE rb_expression, builder;
174
+
175
+ rb_scan_args(argc, argv, "11", &rb_query, &options);
176
+
177
+ rb_grn_column_deconstruct(SELF(self), &column, &context,
178
+ NULL, NULL,
179
+ NULL, NULL, NULL);
180
+ table = grn_column_table(context, column);
181
+
182
+ rb_grn_scan_options(options,
183
+ "operator", &rb_operator,
184
+ "result", &rb_result,
185
+ "name", &rb_name,
186
+ NULL);
187
+
188
+ if (!NIL_P(rb_operator))
189
+ operator = NUM2INT(rb_operator);
190
+
191
+ if (NIL_P(rb_result)) {
192
+ result = grn_table_create(context, NULL, 0, NULL,
193
+ GRN_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
194
+ table,
195
+ 0);
196
+ rb_result = GRNTABLE2RVAL(context, result, RB_GRN_TRUE);
197
+ } else {
198
+ result = RVAL2GRNTABLE(rb_result, &context);
199
+ }
200
+
201
+ builder = rb_grn_column_expression_builder_new(self, rb_name, rb_query);
202
+ rb_expression = rb_grn_column_expression_builder_build(builder);
203
+
204
+ rb_grn_object_deconstruct(RB_GRN_OBJECT(DATA_PTR(rb_expression)),
205
+ &expression, NULL,
206
+ NULL, NULL, NULL, NULL);
207
+
208
+ rc = grn_table_select(context, table, expression, result, operator);
209
+ rb_grn_context_check(context, self);
210
+ rb_grn_rc_check(rc, self);
211
+
212
+ return rb_result;
213
+ }
214
+
90
215
  void
91
216
  rb_grn_init_column (VALUE mGrn)
92
217
  {
93
218
  rb_cGrnColumn = rb_define_class_under(mGrn, "Column", rb_cGrnObject);
94
219
 
95
- rb_cGrnVarSizeColumn =
96
- rb_define_class_under(mGrn, "VarSizeColumn", rb_cGrnColumn);
97
-
98
220
  rb_define_method(rb_cGrnColumn, "table", rb_grn_column_get_table, 0);
221
+ rb_define_method(rb_cGrnColumn, "local_name",
222
+ rb_grn_column_get_local_name, 0);
223
+
224
+ rb_define_method(rb_cGrnColumn, "select", rb_grn_column_select, -1);
99
225
 
100
226
  rb_grn_init_fix_size_column(mGrn);
227
+ rb_grn_init_variable_size_column(mGrn);
101
228
  rb_grn_init_index_column(mGrn);
102
229
  }
data/ext/rb-grn-context.c CHANGED
@@ -20,8 +20,6 @@
20
20
 
21
21
  #define SELF(object) (RVAL2GRNCONTEXT(object))
22
22
 
23
- #define OBJECTS_TABLE_NAME "<ranguba:objects>"
24
-
25
23
  static VALUE cGrnContext;
26
24
 
27
25
  /*
@@ -58,90 +56,23 @@ rb_grn_context_from_ruby_object (VALUE object)
58
56
  return context;
59
57
  }
60
58
 
61
- void
62
- rb_grn_context_register (grn_ctx *context, RbGrnObject *object)
63
- {
64
- grn_obj *objects;
65
-
66
- if (!grn_ctx_db(context))
67
- return;
68
-
69
- objects = grn_ctx_get(context,
70
- OBJECTS_TABLE_NAME,
71
- strlen(OBJECTS_TABLE_NAME));
72
- if (!objects)
73
- objects = grn_table_create(context,
74
- OBJECTS_TABLE_NAME,
75
- strlen(OBJECTS_TABLE_NAME),
76
- NULL,
77
- GRN_OBJ_TABLE_HASH_KEY,
78
- grn_ctx_at(context, GRN_DB_UINT64),
79
- 0);
80
-
81
- grn_table_add(context, objects,
82
- &object, sizeof(RbGrnObject *),
83
- NULL);
84
- }
85
-
86
- void
87
- rb_grn_context_unregister (grn_ctx *context, RbGrnObject *object)
88
- {
89
- grn_obj *objects;
90
-
91
- if (!grn_ctx_db(context))
92
- return;
93
-
94
- objects = grn_ctx_get(context,
95
- OBJECTS_TABLE_NAME,
96
- strlen(OBJECTS_TABLE_NAME));
97
- if (!objects)
98
- return;
99
-
100
- grn_table_delete(context, objects, &object, sizeof(RbGrnObject *));
101
- }
102
-
103
- void
104
- rb_grn_context_unbind (grn_ctx *context)
105
- {
106
- grn_obj *database;
107
- grn_obj *objects;
108
-
109
- database = grn_ctx_db(context);
110
- if (!database)
111
- return;
112
-
113
- objects = grn_ctx_get(context,
114
- OBJECTS_TABLE_NAME,
115
- strlen(OBJECTS_TABLE_NAME));
116
- if (objects) {
117
- grn_table_cursor *cursor;
118
-
119
- cursor = grn_table_cursor_open(context, objects, NULL, 0, NULL, 0, 0);
120
- while (grn_table_cursor_next(context, cursor) != GRN_ID_NIL) {
121
- void *value;
122
- RbGrnObject *object = NULL;
123
- grn_table_cursor_get_key(context, cursor, &value);
124
- memcpy(&object, value, sizeof(RbGrnObject *));
125
- object->object = NULL;
126
- object->unbind(object);
127
- }
128
- grn_table_cursor_close(context, cursor);
129
- grn_obj_close(context, objects);
130
- }
131
-
132
- grn_obj_close(context, database);
133
- grn_ctx_use(context, NULL);
134
- }
135
-
136
59
  static void
137
60
  rb_grn_context_free (void *pointer)
138
61
  {
139
62
  grn_ctx *context = pointer;
140
63
 
64
+ debug("context-free: %p\n", context);
141
65
  if (context->stat != GRN_CTX_FIN) {
142
- rb_grn_context_unbind (context);
66
+ grn_obj *database;
67
+
68
+ database = grn_ctx_db(context);
69
+ debug("context:database: %p:%p\n", context, database);
70
+ if (database && database->header.type == GRN_DB) {
71
+ grn_obj_close(context, database);
72
+ }
143
73
  grn_ctx_fin(context);
144
74
  }
75
+ debug("context-free: %p: done\n", context);
145
76
  xfree(context);
146
77
  }
147
78
 
@@ -326,6 +257,8 @@ rb_grn_context_initialize (int argc, VALUE *argv, VALUE self)
326
257
  GRN_CTX_SET_ENCODING(context, encoding);
327
258
  }
328
259
 
260
+ debug("context new: %p\n", context);
261
+
329
262
  return Qnil;
330
263
  }
331
264
 
@@ -410,6 +343,77 @@ rb_grn_context_get_database (VALUE self)
410
343
  return GRNDB2RVAL(context, grn_ctx_db(context), RB_GRN_FALSE);
411
344
  }
412
345
 
346
+ static const char *
347
+ grn_type_name_old_to_new (const char *name, unsigned int name_size)
348
+ {
349
+ unsigned int i;
350
+
351
+ for (i = 0; i < name_size; i++) {
352
+ if (name[i] == '\0')
353
+ return NULL;
354
+ }
355
+
356
+ if (strcmp(name, "<int>") == 0) {
357
+ return "Int32";
358
+ } else if (strcmp(name, "<uint>") == 0) {
359
+ return "UInt32";
360
+ } else if (strcmp(name, "<int64>") == 0) {
361
+ return "Int64";
362
+ } else if (strcmp(name, "<uint64>") == 0) {
363
+ return "UInt64";
364
+ } else if (strcmp(name, "<float>") == 0) {
365
+ return "Float";
366
+ } else if (strcmp(name, "<time>") == 0) {
367
+ return "Time";
368
+ } else if (strcmp(name, "<shorttext>") == 0) {
369
+ return "ShortText";
370
+ } else if (strcmp(name, "<text>") == 0) {
371
+ return "Text";
372
+ } else if (strcmp(name, "<longtext>") == 0) {
373
+ return "LongText";
374
+ } else if (strcmp(name, "<token:delimit>") == 0) {
375
+ return "TokenDelimit";
376
+ } else if (strcmp(name, "<token:unigram>") == 0) {
377
+ return "TokenUnigram";
378
+ } else if (strcmp(name, "<token:bigram>") == 0) {
379
+ return "TokenBigram";
380
+ } else if (strcmp(name, "<token:trigram>") == 0) {
381
+ return "TokenTrigram";
382
+ } else if (strcmp(name, "<token:mecab>") == 0) {
383
+ return "TokenMecab";
384
+ }
385
+
386
+ return NULL;
387
+ }
388
+
389
+ grn_obj *
390
+ rb_grn_context_get_backward_compatibility (grn_ctx *context,
391
+ const char *name,
392
+ unsigned int name_size)
393
+ {
394
+ grn_obj *object;
395
+
396
+ object = grn_ctx_get(context, name, name_size);
397
+ if (!object) {
398
+ const char *new_type_name;
399
+
400
+ new_type_name = grn_type_name_old_to_new(name, name_size);
401
+ if (new_type_name) {
402
+ object = grn_ctx_get(context, new_type_name, strlen(new_type_name));
403
+ #if 0
404
+ if (object) {
405
+ rb_warn("deprecated old data type name <%s> is used. "
406
+ "Use new data type name <%s> instead.",
407
+ name, new_type_name);
408
+ }
409
+ #endif
410
+ }
411
+ }
412
+
413
+ return object;
414
+ }
415
+
416
+
413
417
  /*
414
418
  * call-seq:
415
419
  * context[name] -> Groonga::Object or nil
@@ -432,11 +436,12 @@ rb_grn_context_array_reference (VALUE self, VALUE name_or_id)
432
436
  context = SELF(self);
433
437
  if (RVAL2CBOOL(rb_obj_is_kind_of(name_or_id, rb_cString))) {
434
438
  const char *name;
435
- unsigned name_size;
439
+ unsigned int name_size;
436
440
 
437
441
  name = StringValuePtr(name_or_id);
438
442
  name_size = RSTRING_LEN(name_or_id);
439
- object = grn_ctx_get(context, name, name_size);
443
+ object = rb_grn_context_get_backward_compatibility(context,
444
+ name, name_size);
440
445
  } else if (RVAL2CBOOL(rb_obj_is_kind_of(name_or_id, rb_cInteger))) {
441
446
  unsigned id;
442
447
  id = NUM2UINT(name_or_id);
@@ -50,6 +50,41 @@ rb_grn_database_to_ruby_object (grn_ctx *context, grn_obj *database,
50
50
  return GRNOBJECT2RVAL(rb_cGrnDatabase, context, database, owner);
51
51
  }
52
52
 
53
+ static void
54
+ rb_grn_database_deconstruct (RbGrnObject *rb_grn_database,
55
+ grn_obj **database,
56
+ grn_ctx **context,
57
+ grn_id *domain_id,
58
+ grn_obj **domain,
59
+ grn_id *range_id,
60
+ grn_obj **range)
61
+ {
62
+ rb_grn_object_deconstruct(rb_grn_database, database, context,
63
+ domain_id, domain,
64
+ range_id, range);
65
+ }
66
+
67
+ /*
68
+ * Document-method: close
69
+ *
70
+ * call-seq:
71
+ * database.close
72
+ *
73
+ * _database_が使用しているリソースを開放する。これ以降_database_を
74
+ * 使うことはできない。
75
+ */
76
+ static VALUE
77
+ rb_grn_database_close (VALUE self)
78
+ {
79
+ VALUE rb_context;
80
+
81
+ rb_context = rb_iv_get(self, "context");
82
+ if (!NIL_P(rb_context))
83
+ rb_iv_set(rb_context, "database", Qnil);
84
+
85
+ return rb_grn_object_close(self);
86
+ }
87
+
53
88
  /*
54
89
  * call-seq:
55
90
  * Groonga::Database.create(options=nil) -> Groonga::Database
@@ -79,7 +114,7 @@ static VALUE
79
114
  rb_grn_database_s_create (int argc, VALUE *argv, VALUE klass)
80
115
  {
81
116
  grn_ctx *context;
82
- grn_obj *database;
117
+ grn_obj *old_database, *database;
83
118
  grn_db_create_optarg create_args;
84
119
  const char *path = NULL;
85
120
  VALUE rb_database;
@@ -100,17 +135,21 @@ rb_grn_database_s_create (int argc, VALUE *argv, VALUE klass)
100
135
  create_args.builtin_type_names = NULL;
101
136
  create_args.n_builtin_type_names = 0;
102
137
 
138
+ old_database = grn_ctx_db(context);
139
+ if (old_database)
140
+ grn_obj_close(context, old_database);
103
141
  database = grn_db_create(context, path, &create_args);
104
142
  rb_grn_context_check(context, rb_ary_new4(argc, argv));
105
143
  rb_database = rb_grn_object_alloc(klass);
106
- rb_grn_object_assign(rb_database, rb_context, context,
107
- database, RB_GRN_TRUE);
144
+ rb_grn_object_assign(Qnil, rb_database, rb_context, context, database);
108
145
  rb_iv_set(rb_database, "context", rb_context);
146
+ if (!NIL_P(rb_context))
147
+ rb_iv_set(rb_context, "database", rb_database);
109
148
  rb_grn_context_check(context, rb_ary_new4(argc, argv));
110
149
 
111
150
  if (rb_block_given_p())
112
151
  return rb_ensure(rb_yield, rb_database,
113
- rb_grn_object_close, rb_database);
152
+ rb_grn_database_close, rb_database);
114
153
  else
115
154
  return rb_database;
116
155
  }
@@ -135,7 +174,7 @@ static VALUE
135
174
  rb_grn_database_initialize (int argc, VALUE *argv, VALUE self)
136
175
  {
137
176
  grn_ctx *context;
138
- grn_obj *database;
177
+ grn_obj *old_database, *database;
139
178
  const char *path;
140
179
  VALUE rb_path, options, rb_context;
141
180
 
@@ -148,10 +187,17 @@ rb_grn_database_initialize (int argc, VALUE *argv, VALUE self)
148
187
 
149
188
  context = rb_grn_context_ensure(&rb_context);
150
189
 
190
+ old_database = grn_ctx_db(context);
191
+ if (old_database)
192
+ grn_obj_close(context, old_database);
151
193
  database = grn_db_open(context, path);
152
- rb_grn_object_assign(self, rb_context, context, database, RB_GRN_TRUE);
194
+ rb_grn_object_assign(Qnil, self, rb_context, context, database);
153
195
  rb_grn_context_check(context, self);
154
196
 
197
+ rb_iv_set(self, "context", rb_context);
198
+ if (!NIL_P(rb_context))
199
+ rb_iv_set(rb_context, "database", self);
200
+
155
201
  return Qnil;
156
202
  }
157
203
 
@@ -179,7 +225,7 @@ rb_grn_database_s_open (int argc, VALUE *argv, VALUE klass)
179
225
  database = rb_grn_object_alloc(klass);
180
226
  rb_grn_database_initialize(argc, argv, database);
181
227
  if (rb_block_given_p())
182
- return rb_ensure(rb_yield, database, rb_grn_object_close, database);
228
+ return rb_ensure(rb_yield, database, rb_grn_database_close, database);
183
229
  else
184
230
  return database;
185
231
  }
@@ -204,8 +250,8 @@ rb_grn_database_each (VALUE self)
204
250
  VALUE rb_cursor;
205
251
  grn_id id;
206
252
 
207
- rb_grn_object_deconstruct((RbGrnObject *)SELF(self), &database, &context,
208
- NULL, NULL, NULL, NULL);
253
+ rb_grn_database_deconstruct(SELF(self), &database, &context,
254
+ NULL, NULL, NULL, NULL);
209
255
  cursor = grn_table_cursor_open(context, database, NULL, 0, NULL, 0, 0);
210
256
  rb_cursor = GRNTABLECURSOR2RVAL(Qnil, context, cursor);
211
257
  rb_iv_set(self, "cursor", rb_cursor);
@@ -222,6 +268,124 @@ rb_grn_database_each (VALUE self)
222
268
  return Qnil;
223
269
  }
224
270
 
271
+ /*
272
+ * Document-method: unlock
273
+ *
274
+ * call-seq:
275
+ * database.unlock
276
+ *
277
+ * _database_のロックを解除する。
278
+ */
279
+ static VALUE
280
+ rb_grn_database_unlock (VALUE self)
281
+ {
282
+ grn_ctx *context;
283
+ grn_obj *database;
284
+ grn_rc rc;
285
+
286
+ rb_grn_database_deconstruct(SELF(self), &database, &context,
287
+ NULL, NULL, NULL, NULL);
288
+
289
+ rc = grn_obj_unlock(context, database, GRN_ID_NIL);
290
+ rb_grn_context_check(context, self);
291
+ rb_grn_rc_check(rc, self);
292
+
293
+ return Qnil;
294
+ }
295
+
296
+ /*
297
+ * Document-method: lock
298
+ *
299
+ * call-seq:
300
+ * database.lock(options={})
301
+ * database.lock(options={}) {...}
302
+ *
303
+ * _database_をロックする。ロックに失敗した場合は
304
+ * Groonga::ResourceDeadlockAvoided例外が発生する。
305
+ *
306
+ * ブロックを指定した場合はブロックを抜けたときにunlockする。
307
+ *
308
+ * 利用可能なオプションは以下の通り。
309
+ *
310
+ * [_:timeout_]
311
+ * ロックを獲得できなかった場合は_:timeout_秒間ロックの獲
312
+ * 得を試みる。_:timeout_秒以内にロックを獲得できなかった
313
+ * 場合は例外が発生する。
314
+ */
315
+ static VALUE
316
+ rb_grn_database_lock (int argc, VALUE *argv, VALUE self)
317
+ {
318
+ grn_ctx *context;
319
+ grn_obj *database;
320
+ int timeout = 0;
321
+ grn_rc rc;
322
+ VALUE options, rb_timeout;
323
+
324
+ rb_scan_args(argc, argv, "01", &options);
325
+
326
+ rb_grn_database_deconstruct(SELF(self), &database, &context,
327
+ NULL, NULL, NULL, NULL);
328
+
329
+ rb_grn_scan_options(options,
330
+ "timeout", &rb_timeout,
331
+ NULL);
332
+
333
+ if (!NIL_P(rb_timeout))
334
+ timeout = NUM2UINT(rb_timeout);
335
+
336
+ rc = grn_obj_lock(context, database, GRN_ID_NIL, timeout);
337
+ rb_grn_context_check(context, self);
338
+ rb_grn_rc_check(rc, self);
339
+
340
+ if (rb_block_given_p()) {
341
+ return rb_ensure(rb_yield, Qnil, rb_grn_database_unlock, self);
342
+ } else {
343
+ return Qnil;
344
+ }
345
+ }
346
+
347
+ /*
348
+ * Document-method: clear_lock
349
+ *
350
+ * call-seq:
351
+ * database.clear_lock
352
+ *
353
+ * _database_のロックを強制的に解除する。
354
+ */
355
+ static VALUE
356
+ rb_grn_database_clear_lock (VALUE self)
357
+ {
358
+ grn_ctx *context;
359
+ grn_obj *database;
360
+
361
+ rb_grn_database_deconstruct(SELF(self), &database, &context,
362
+ NULL, NULL, NULL, NULL);
363
+
364
+ grn_obj_clear_lock(context, database);
365
+
366
+ return Qnil;
367
+ }
368
+
369
+ /*
370
+ * Document-method: locked?
371
+ *
372
+ * call-seq:
373
+ * database.locked?
374
+ *
375
+ * _database_がロックされていれば+true+を返す。
376
+ */
377
+ static VALUE
378
+ rb_grn_database_is_locked (VALUE self)
379
+ {
380
+ grn_ctx *context;
381
+ grn_obj *database;
382
+
383
+ rb_grn_database_deconstruct(SELF(self), &database, &context,
384
+ NULL, NULL, NULL, NULL);
385
+
386
+ return CBOOL2RVAL(grn_obj_is_locked(context, database));
387
+ }
388
+
225
389
  void
226
390
  rb_grn_init_database (VALUE mGrn)
227
391
  {
@@ -239,4 +403,13 @@ rb_grn_init_database (VALUE mGrn)
239
403
 
240
404
  rb_define_method(rb_cGrnDatabase, "each",
241
405
  rb_grn_database_each, 0);
406
+
407
+ rb_define_method(rb_cGrnDatabase, "close",
408
+ rb_grn_database_close, 0);
409
+
410
+ rb_define_method(rb_cGrnDatabase, "lock", rb_grn_database_lock, -1);
411
+ rb_define_method(rb_cGrnDatabase, "unlock", rb_grn_database_unlock, 0);
412
+ rb_define_method(rb_cGrnDatabase, "clear_lock",
413
+ rb_grn_database_clear_lock, 0);
414
+ rb_define_method(rb_cGrnDatabase, "locked?", rb_grn_database_is_locked, 0);
242
415
  }