groonga 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
  }