Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
dat.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 2 -*- */
2 /* Copyright(C) 2011-2012 Brazil
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Lesser General Public
6  License version 2.1 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Lesser General Public License for more details.
12 
13  You should have received a copy of the GNU Lesser General Public
14  License along with this library; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 #include "groonga_in.h"
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <cstring>
21 #include <new>
22 #include "str.h"
23 #include "io.h"
24 #include "dat.h"
25 #include "util.h"
26 #include "normalizer_in.h"
27 
28 #include "dat/trie.hpp"
29 #include "dat/cursor-factory.hpp"
30 
31 namespace {
32 
33 const uint32_t FILE_ID_LENGTH = 3;
34 
35 class CriticalSection {
36  public:
37  CriticalSection() : lock_(NULL) {}
38  explicit CriticalSection(grn_critical_section *lock) : lock_(lock) {
39  CRITICAL_SECTION_ENTER(*lock_);
40  }
41  ~CriticalSection() {
42  leave();
43  }
44 
45  void enter(grn_critical_section *lock) {
46  leave();
47  lock_ = lock;
48  }
49  void leave() {
50  if (lock_ != NULL) {
51  CRITICAL_SECTION_LEAVE(*lock_);
52  lock_ = NULL;
53  }
54  }
55 
56  private:
57  grn_critical_section *lock_;
58 
59  // Disallows copy and assignment.
60  CriticalSection(const CriticalSection &);
61  CriticalSection &operator=(const CriticalSection &);
62 };
63 
64 /*
65  grn_dat_remove_file() removes a file specified by `path' and then returns
66  true on success, false on failure. Note that grn_dat_remove_file() does not
67  change `ctx->rc'.
68  */
69 bool
70 grn_dat_remove_file(grn_ctx *ctx, const char *path)
71 {
72  struct stat stat;
73  return !::stat(path, &stat) && !unlink(path);
74 }
75 
76 grn_rc
77 grn_dat_translate_error_code(grn::dat::ErrorCode error_code) {
78  switch (error_code) {
79  case grn::dat::PARAM_ERROR: {
80  return GRN_INVALID_ARGUMENT;
81  }
82  case grn::dat::IO_ERROR: {
84  }
86  return GRN_INVALID_FORMAT;
87  }
90  }
93  return GRN_UNKNOWN_ERROR;
94  }
96  return GRN_FILE_CORRUPT;
97  }
98  default: {
99  return GRN_UNKNOWN_ERROR;
100  }
101  }
102 }
103 
104 void
105 grn_dat_init(grn_ctx *, grn_dat *dat)
106 {
108  dat->io = NULL;
109  dat->header = NULL;
110  dat->file_id = 0;
111  dat->encoding = GRN_ENC_DEFAULT;
112  dat->trie = NULL;
113  dat->old_trie = NULL;
114  dat->tokenizer = NULL;
115  CRITICAL_SECTION_INIT(dat->lock);
116 }
117 
118 void
119 grn_dat_fin(grn_ctx *ctx, grn_dat *dat)
120 {
121  CRITICAL_SECTION_FIN(dat->lock);
122  delete static_cast<grn::dat::Trie *>(dat->old_trie);
123  delete static_cast<grn::dat::Trie *>(dat->trie);
124  dat->old_trie = NULL;
125  dat->trie = NULL;
126  if (dat->io) {
127  grn_io_close(ctx, dat->io);
128  dat->io = NULL;
129  }
130 }
131 
132 /*
133  grn_dat_generate_trie_path() generates the path from `base_path' and
134  `file_id'. The generated path is stored in `trie_path'.
135  */
136 void
137 grn_dat_generate_trie_path(const char *base_path, char *trie_path, uint32_t file_id)
138 {
139  if (!base_path || !base_path[0]) {
140  trie_path[0] = '\0';
141  return;
142  }
143  const size_t len = std::strlen(base_path);
144  std::memcpy(trie_path, base_path, len);
145  trie_path[len] = '.';
146  grn_itoh(file_id % (1U << (4 * FILE_ID_LENGTH)),
147  trie_path + len + 1, FILE_ID_LENGTH);
148  trie_path[len + 1 + FILE_ID_LENGTH] = '\0';
149 }
150 
151 bool
152 grn_dat_open_trie_if_needed(grn_ctx *ctx, grn_dat *dat)
153 {
154  if (!dat) {
155  ERR(GRN_INVALID_ARGUMENT, "dat is null");
156  return false;
157  }
158 
159  const uint32_t file_id = dat->header->file_id;
160  if (!file_id || (dat->trie && (file_id <= dat->file_id))) {
161  /*
162  There is no need to open file when no trie file is available or the
163  current trie file is the latest one.
164  */
165  return true;
166  }
167 
168  CriticalSection critical_section(&dat->lock);
169 
170  if (dat->trie && (file_id <= dat->file_id)) {
171  /*
172  There is no need to open file if the latest file has been opened by
173  another thread.
174  */
175  return true;
176  }
177 
178  char trie_path[PATH_MAX];
179  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id);
180  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
181  grn::dat::Trie * const old_trie = static_cast<grn::dat::Trie *>(dat->old_trie);
182  grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
183  if (!new_trie) {
184  MERR("new grn::dat::Trie failed");
185  return false;
186  }
187 
188  try {
189  new_trie->open(trie_path);
190  } catch (const grn::dat::Exception &ex) {
191  ERR(grn_dat_translate_error_code(ex.code()),
192  "grn::dat::Trie::open failed");
193  delete new_trie;
194  return false;
195  }
196 
197  dat->old_trie = trie;
198  dat->trie = new_trie;
199  dat->file_id = file_id;
200 
201  critical_section.leave();
202 
203  delete old_trie;
204  if (file_id >= 3) {
205  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id - 2);
206  grn_dat_remove_file(ctx, trie_path);
207  }
208  return true;
209 }
210 
211 bool grn_dat_rebuild_trie(grn_ctx *ctx, grn_dat *dat) {
212  grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
213  if (!new_trie) {
214  MERR("new grn::dat::Trie failed");
215  return false;
216  }
217 
218  const uint32_t file_id = dat->header->file_id;
219  try {
220  char trie_path[PATH_MAX];
221  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id + 1);
222  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
223  new_trie->create(*trie, trie_path, trie->file_size() * 2);
224  } catch (const grn::dat::Exception &ex) {
225  ERR(grn_dat_translate_error_code(ex.code()),
226  "grn::dat::Trie::open failed");
227  delete new_trie;
228  return false;
229  }
230 
231  grn::dat::Trie * const old_trie = static_cast<grn::dat::Trie *>(dat->old_trie);
232  dat->old_trie = dat->trie;
233  dat->trie = new_trie;
234  dat->header->file_id = dat->file_id = file_id + 1;
235 
236  delete old_trie;
237  if (file_id >= 2) {
238  char trie_path[PATH_MAX];
239  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id - 1);
240  grn_dat_remove_file(ctx, trie_path);
241  }
242  return true;
243 }
244 
245 void grn_dat_cursor_init(grn_ctx *, grn_dat_cursor *cursor) {
247  cursor->dat = NULL;
248  cursor->cursor = NULL;
249  cursor->key = &grn::dat::Key::invalid_key();
250  cursor->curr_rec = GRN_ID_NIL;
251 }
252 
253 void grn_dat_cursor_fin(grn_ctx *, grn_dat_cursor *cursor) {
254  delete static_cast<grn::dat::Cursor *>(cursor->cursor);
255  cursor->dat = NULL;
256  cursor->cursor = NULL;
257  cursor->key = &grn::dat::Key::invalid_key();
258  cursor->curr_rec = GRN_ID_NIL;
259 }
260 
261 } // namespace
262 
263 extern "C" {
264 
265 grn_dat *
266 grn_dat_create(grn_ctx *ctx, const char *path, uint32_t,
267  uint32_t, uint32_t flags)
268 {
269  if (path) {
270  if (path[0] == '\0') {
271  path = NULL;
272  } else if (std::strlen(path) >= (PATH_MAX - (FILE_ID_LENGTH + 1))) {
273  ERR(GRN_FILENAME_TOO_LONG, "too long path");
274  return NULL;
275  }
276  }
277 
278  grn_dat * const dat = static_cast<grn_dat *>(GRN_MALLOC(sizeof(grn_dat)));
279  if (!dat) {
280  return NULL;
281  }
282  grn_dat_init(ctx, dat);
283 
284  dat->io = grn_io_create(ctx, path, sizeof(struct grn_dat_header),
286  if (!dat->io) {
287  GRN_FREE(dat);
288  return NULL;
289  }
291 
292  dat->header = static_cast<struct grn_dat_header *>(grn_io_header(dat->io));
293  if (!dat->header) {
294  grn_io_close(ctx, dat->io);
295  grn_dat_remove_file(ctx, path);
296  GRN_FREE(dat);
297  return NULL;
298  }
299  const grn_encoding encoding = (ctx->encoding != GRN_ENC_DEFAULT) ?
300  ctx->encoding : grn_gctx.encoding;
301  dat->header->flags = flags;
302  dat->header->encoding = encoding;
303  dat->header->tokenizer = GRN_ID_NIL;
304  dat->header->file_id = 0;
305  if (dat->header->flags & GRN_OBJ_KEY_NORMALIZE) {
308  dat->header->normalizer = grn_obj_id(ctx, dat->normalizer);
309  } else {
310  dat->normalizer = NULL;
311  dat->header->normalizer = GRN_ID_NIL;
312  }
313  dat->encoding = encoding;
314  dat->tokenizer = NULL;
315 
316  dat->obj.header.flags = dat->header->flags;
317 
318  return dat;
319 }
320 
321 grn_dat *
322 grn_dat_open(grn_ctx *ctx, const char *path)
323 {
324  if (path && (std::strlen(path) >= (PATH_MAX - (FILE_ID_LENGTH + 1)))) {
325  ERR(GRN_FILENAME_TOO_LONG, "too long path");
326  return NULL;
327  }
328 
329  grn_dat * const dat = static_cast<grn_dat *>(GRN_MALLOC(sizeof(grn_dat)));
330  if (!dat) {
331  return NULL;
332  }
333 
334  grn_dat_init(ctx, dat);
335  dat->io = grn_io_open(ctx, path, grn_io_auto);
336  if (!dat->io) {
337  GRN_FREE(dat);
338  return NULL;
339  }
340 
341  dat->header = (struct grn_dat_header *)grn_io_header(dat->io);
342  if (!dat->header) {
343  grn_io_close(ctx, dat->io);
344  GRN_FREE(dat);
345  return NULL;
346  }
347  dat->file_id = dat->header->file_id;
348  dat->encoding = dat->header->encoding;
349  dat->tokenizer = grn_ctx_at(ctx, dat->header->tokenizer);
350  if (dat->header->flags & GRN_OBJ_KEY_NORMALIZE) {
353  dat->header->normalizer = grn_obj_id(ctx, dat->normalizer);
354  } else {
355  dat->normalizer = grn_ctx_at(ctx, dat->header->normalizer);
356  }
357  dat->obj.header.flags = dat->header->flags;
358  return dat;
359 }
360 
361 grn_rc
363 {
364  if (dat) {
365  grn_dat_fin(ctx, dat);
366  GRN_FREE(dat);
367  }
368  return GRN_SUCCESS;
369 }
370 
371 grn_rc
372 grn_dat_remove(grn_ctx *ctx, const char *path)
373 {
374  if (!path) {
375  ERR(GRN_INVALID_ARGUMENT, "path is null");
376  return GRN_INVALID_ARGUMENT;
377  }
378 
379  grn_dat * const dat = grn_dat_open(ctx, path);
380  if (!dat) {
381  return ctx->rc;
382  }
383  const uint32_t file_id = dat->header->file_id;
384  grn_dat_close(ctx, dat);
385 
386  /*
387  grn_dat_remove() tries to remove (file_id + 1)th trie file because
388  grn::dat::Trie::create() might leave an incomplete file on failure.
389  */
390  char trie_path[PATH_MAX];
391  grn_dat_generate_trie_path(path, trie_path, file_id + 1);
392  grn_dat_remove_file(ctx, trie_path);
393  for (uint32_t i = file_id; i > 0; --i) {
394  grn_dat_generate_trie_path(path, trie_path, i);
395  if (!grn_dat_remove_file(ctx, trie_path)) {
396  break;
397  }
398  }
399 
400  /*
401  grn_io_remove() reports an error when it fails to remove `path'.
402  */
403  return grn_io_remove(ctx, path);
404 }
405 
406 grn_id
407 grn_dat_get(grn_ctx *ctx, grn_dat *dat, const void *key,
408  unsigned int key_size, void **)
409 {
410  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
411  return GRN_ID_NIL;
412  }
413  const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
414  if (!trie) {
415  return GRN_ID_NIL;
416  }
417  grn::dat::UInt32 key_pos;
418  try {
419  if (trie->search(key, key_size, &key_pos)) {
420  return trie->get_key(key_pos).id();
421  }
422  } catch (const grn::dat::Exception &ex) {
423  ERR(grn_dat_translate_error_code(ex.code()),
424  "grn::dat::Trie::search failed");
425  }
426  return GRN_ID_NIL;
427 }
428 
429 grn_id
430 grn_dat_add(grn_ctx *ctx, grn_dat *dat, const void *key,
431  unsigned int key_size, void **, int *added)
432 {
433  if (!key_size) {
434  return GRN_ID_NIL;
435  } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
436  return GRN_ID_NIL;
437  }
438 
439  if (!dat->trie) {
440  char trie_path[PATH_MAX];
441  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, 1);
442  grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
443  if (!new_trie) {
444  MERR("new grn::dat::Trie failed");
445  return GRN_ID_NIL;
446  }
447  try {
448  new_trie->create(trie_path);
449  } catch (const grn::dat::Exception &ex) {
450  ERR(grn_dat_translate_error_code(ex.code()),
451  "grn::dat::Trie::create failed");
452  delete new_trie;
453  return GRN_ID_NIL;
454  }
455  dat->trie = new_trie;
456  dat->file_id = dat->header->file_id = 1;
457  }
458 
459  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
460  try {
461  grn::dat::UInt32 key_pos;
462  const bool res = trie->insert(key, key_size, &key_pos);
463  if (added) {
464  *added = res ? 1 : 0;
465  }
466  return trie->get_key(key_pos).id();
467  } catch (const grn::dat::SizeError &) {
468  if (!grn_dat_rebuild_trie(ctx, dat)) {
469  return GRN_ID_NIL;
470  }
471  grn::dat::Trie * const new_trie = static_cast<grn::dat::Trie *>(dat->trie);
472  grn::dat::UInt32 key_pos;
473  const bool res = new_trie->insert(key, key_size, &key_pos);
474  if (added) {
475  *added = res ? 1 : 0;
476  }
477  return new_trie->get_key(key_pos).id();
478  } catch (const grn::dat::Exception &ex) {
479  ERR(grn_dat_translate_error_code(ex.code()),
480  "grn::dat::Trie::insert failed");
481  return GRN_ID_NIL;
482  }
483 }
484 
485 int
486 grn_dat_get_key(grn_ctx *ctx, grn_dat *dat, grn_id id, void *keybuf, int bufsize)
487 {
488  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
489  return 0;
490  }
491  const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
492  if (!trie) {
493  return 0;
494  }
495  const grn::dat::Key &key = trie->ith_key(id);
496  if (!key.is_valid()) {
497  return 0;
498  }
499  if (keybuf && (bufsize >= (int)key.length())) {
500  std::memcpy(keybuf, key.ptr(), key.length());
501  }
502  return (int)key.length();
503 }
504 
505 int
507 {
508  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
509  return 0;
510  }
511  const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
512  if (!trie) {
513  return 0;
514  }
515  const grn::dat::Key &key = trie->ith_key(id);
516  if (!key.is_valid()) {
517  return 0;
518  }
519  if (bulk->header.impl_flags & GRN_OBJ_REFER) {
520  bulk->u.b.head = static_cast<char *>(const_cast<void *>(key.ptr()));
521  bulk->u.b.curr = bulk->u.b.head + key.length();
522  } else {
523  grn_bulk_write(ctx, bulk, static_cast<const char *>(key.ptr()), key.length());
524  }
525  return (int)key.length();
526 }
527 
528 grn_rc
530  grn_table_delete_optarg *optarg)
531 {
532  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
533  return ctx->rc;
534  } else if (!dat->trie || (id == GRN_ID_NIL)) {
535  return GRN_INVALID_ARGUMENT;
536  }
537 
538  if (optarg && optarg->func) {
539  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
540  if (!trie->ith_entry(id).is_valid()) {
541  return GRN_INVALID_ARGUMENT;
542  } else if (!optarg->func(ctx, reinterpret_cast<grn_obj *>(dat), id, optarg->func_arg)) {
543  return GRN_SUCCESS;
544  }
545  }
546 
547  try {
548  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
549  if (!trie->remove(id)) {
550  return GRN_INVALID_ARGUMENT;
551  }
552  } catch (const grn::dat::Exception &ex) {
553  ERR(grn_dat_translate_error_code(ex.code()),
554  "grn::dat::Trie::remove failed");
555  return ctx->rc;
556  }
557  return GRN_SUCCESS;
558 }
559 
560 grn_rc
561 grn_dat_delete(grn_ctx *ctx, grn_dat *dat, const void *key, unsigned int key_size,
562  grn_table_delete_optarg *optarg)
563 {
564  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
565  return ctx->rc;
566  } else if (!dat->trie || !key || !key_size) {
567  return GRN_INVALID_ARGUMENT;
568  }
569 
570  if (optarg && optarg->func) {
571  try {
572  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
573  grn::dat::UInt32 key_pos;
574  if (!trie->search(key, key_size, &key_pos)) {
575  return GRN_INVALID_ARGUMENT;
576  } else if (!optarg->func(ctx, reinterpret_cast<grn_obj *>(dat),
577  trie->get_key(key_pos).id(), optarg->func_arg)) {
578  return GRN_SUCCESS;
579  }
580  } catch (const grn::dat::Exception &ex) {
581  ERR(grn_dat_translate_error_code(ex.code()),
582  "grn::dat::Trie::search failed");
583  return ctx->rc;
584  }
585  }
586 
587  try {
588  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
589  if (!trie->remove(key, key_size)) {
590  return GRN_INVALID_ARGUMENT;
591  }
592  } catch (const grn::dat::Exception &ex) {
593  ERR(grn_dat_translate_error_code(ex.code()),
594  "grn::dat::Trie::remove failed");
595  return ctx->rc;
596  }
597  return GRN_SUCCESS;
598 }
599 
600 grn_rc
602  const void *dest_key, unsigned int dest_key_size)
603 {
604  if (!dest_key_size) {
605  return GRN_INVALID_ARGUMENT;
606  } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
607  return ctx->rc;
608  } else if (!dat->trie) {
609  return GRN_INVALID_ARGUMENT;
610  }
611  try {
612  try {
613  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
614  if (!trie->update(src_key_id, dest_key, dest_key_size)) {
615  return GRN_INVALID_ARGUMENT;
616  }
617  } catch (const grn::dat::SizeError &) {
618  if (!grn_dat_rebuild_trie(ctx, dat)) {
619  return ctx->rc;
620  }
621  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
622  if (!trie->update(src_key_id, dest_key, dest_key_size)) {
623  return GRN_INVALID_ARGUMENT;
624  }
625  }
626  } catch (const grn::dat::Exception &ex) {
627  ERR(grn_dat_translate_error_code(ex.code()),
628  "grn::dat::Trie::update failed");
629  return ctx->rc;
630  }
631  return GRN_SUCCESS;
632 }
633 
634 grn_rc
636  const void *src_key, unsigned int src_key_size,
637  const void *dest_key, unsigned int dest_key_size)
638 {
639  if (!dest_key_size) {
640  return GRN_INVALID_ARGUMENT;
641  } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
642  return ctx->rc;
643  } else if (!dat->trie) {
644  return GRN_INVALID_ARGUMENT;
645  }
646  try {
647  try {
648  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
649  if (!trie->update(src_key, src_key_size, dest_key, dest_key_size)) {
650  return GRN_INVALID_ARGUMENT;
651  }
652  } catch (const grn::dat::SizeError &) {
653  if (!grn_dat_rebuild_trie(ctx, dat)) {
654  return ctx->rc;
655  }
656  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
657  if (!trie->update(src_key, src_key_size, dest_key, dest_key_size)) {
658  return GRN_INVALID_ARGUMENT;
659  }
660  }
661  } catch (const grn::dat::Exception &ex) {
662  ERR(grn_dat_translate_error_code(ex.code()),
663  "grn::dat::Trie::update failed");
664  return ctx->rc;
665  }
666  return GRN_SUCCESS;
667 }
668 
669 int
670 grn_dat_scan(grn_ctx *ctx, grn_dat *dat, const char *str,
671  unsigned int str_size, grn_dat_scan_hit *scan_hits,
672  unsigned int max_num_scan_hits, const char **str_rest)
673 {
674  if (!grn_dat_open_trie_if_needed(ctx, dat) || !str ||
675  !(dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) || !scan_hits) {
676  return -1;
677  }
678 
679  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
680  if (!trie) {
681  return -1;
682  }
683 
684  if (!max_num_scan_hits || !str_size) {
685  if (str_rest) {
686  *str_rest = str;
687  }
688  return 0;
689  }
690 
691  unsigned int num_scan_hits = 0;
692  try {
693  if (dat->normalizer) {
695  grn_obj * const normalized_string = grn_string_open(ctx, str, str_size,
696  dat->normalizer,
697  flags);
698  if (!normalized_string) {
699  fprintf(stderr, "error: grn_string_open() failed!\n");
700  return -1;
701  }
702  grn_string_get_normalized(ctx, normalized_string, &str, &str_size, NULL);
703  const short *checks = grn_string_get_checks(ctx, normalized_string);
704  unsigned int offset = 0;
705  while (str_size) {
706  if (*checks) {
707  grn::dat::UInt32 key_pos;
708  if (trie->lcp_search(str, str_size, &key_pos)) {
709  const grn::dat::Key &key = trie->get_key(key_pos);
710  const grn::dat::UInt32 key_length = key.length();
711  if ((key_length == str_size) || (checks[key_length])) {
712  unsigned int length = 0;
713  for (grn::dat::UInt32 i = 0; i < key_length; ++i) {
714  if (checks[i] > 0) {
715  length += checks[i];
716  }
717  }
718  scan_hits[num_scan_hits].id = key.id();
719  scan_hits[num_scan_hits].offset = offset;
720  scan_hits[num_scan_hits].length = length;
721  offset += length;
722  str += key_length;
723  str_size -= key_length;
724  checks += key_length;
725  if (++num_scan_hits >= max_num_scan_hits) {
726  break;
727  }
728  continue;
729  }
730  }
731  if (*checks > 0) {
732  offset += *checks;
733  }
734  }
735  ++str;
736  --str_size;
737  ++checks;
738  }
739  if (str_rest) {
740  grn_string_get_original(ctx, normalized_string, str_rest, NULL);
741  *str_rest += offset;
742  }
743  grn_obj_close(ctx, normalized_string);
744  } else {
745  const char * const begin = str;
746  while (str_size) {
747  grn::dat::UInt32 key_pos;
748  if (trie->lcp_search(str, str_size, &key_pos)) {
749  const grn::dat::Key &key = trie->get_key(key_pos);
750  scan_hits[num_scan_hits].id = key.id();
751  scan_hits[num_scan_hits].offset = str - begin;
752  scan_hits[num_scan_hits].length = key.length();
753  str += key.length();
754  str_size -= key.length();
755  if (++num_scan_hits >= max_num_scan_hits) {
756  break;
757  }
758  } else {
759  const int char_length = grn_charlen(ctx, str, str + str_size);
760  if (char_length) {
761  str += char_length;
762  str_size -= char_length;
763  } else {
764  ++str;
765  --str_size;
766  }
767  }
768  }
769  if (str_rest) {
770  *str_rest = str;
771  }
772  }
773  } catch (const grn::dat::Exception &ex) {
774  ERR(grn_dat_translate_error_code(ex.code()),
775  "grn::dat::lcp_search failed");
776  return -1;
777  }
778  return static_cast<int>(num_scan_hits);
779 }
780 
781 grn_id
783  const void *key, unsigned int key_size)
784 {
785  if (!grn_dat_open_trie_if_needed(ctx, dat) || !key ||
786  !(dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
787  return GRN_ID_NIL;
788  }
789 
790  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
791  if (!trie) {
792  return GRN_ID_NIL;
793  }
794 
795  try {
796  grn::dat::UInt32 key_pos;
797  if (!trie->lcp_search(key, key_size, &key_pos)) {
798  return GRN_ID_NIL;
799  }
800  return trie->get_key(key_pos).id();
801  } catch (const grn::dat::Exception &ex) {
802  ERR(grn_dat_translate_error_code(ex.code()),
803  "grn::dat::PrefixCursor::open failed");
804  return GRN_ID_NIL;
805  }
806 }
807 
808 unsigned int
810 {
811  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
812  return 0;
813  }
814  const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
815  if (trie) {
816  return trie->num_keys();
817  }
818  return 0;
819 }
820 
823  const void *min, unsigned int min_size,
824  const void *max, unsigned int max_size,
825  int offset, int limit, int flags)
826 {
827  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
828  return NULL;
829  }
830 
831  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
832  if (!trie) {
833  grn_dat_cursor * const dc =
834  static_cast<grn_dat_cursor *>(GRN_MALLOC(sizeof(grn_dat_cursor)));
835  if (dc) {
836  grn_dat_cursor_init(ctx, dc);
837  }
838  return dc;
839  }
840 
841  grn_dat_cursor * const dc =
842  static_cast<grn_dat_cursor *>(GRN_MALLOC(sizeof(grn_dat_cursor)));
843  if (!dc) {
844  return NULL;
845  }
846  grn_dat_cursor_init(ctx, dc);
847 
848  try {
849  if ((flags & GRN_CURSOR_BY_ID) != 0) {
851  min, min_size, max, max_size, offset, limit,
854  ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_LOWER_BOUND : 0) |
855  ((flags & GRN_CURSOR_LT) ? grn::dat::EXCEPT_UPPER_BOUND : 0));
856  } else if ((flags & GRN_CURSOR_PREFIX) != 0) {
857  if (max && max_size) {
858  if ((dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) != 0) {
860  NULL, min_size, max, max_size, offset, limit,
862  } else {
863  // TODO: near
864  }
865  } else if (min && min_size) {
866  if ((flags & GRN_CURSOR_RK) != 0) {
867  // TODO: rk search
868  } else {
870  min, min_size, NULL, 0, offset, limit,
873  ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_EXACT_MATCH : 0));
874  }
875  }
876  } else {
878  min, min_size, max, max_size, offset, limit,
881  ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_LOWER_BOUND : 0) |
882  ((flags & GRN_CURSOR_LT) ? grn::dat::EXCEPT_UPPER_BOUND : 0));
883  }
884  } catch (const grn::dat::Exception &ex) {
885  ERR(grn_dat_translate_error_code(ex.code()),
886  "grn::dat::CursorFactory::open failed");
887  GRN_FREE(dc);
888  return NULL;
889  }
890  if (!dc->cursor) {
891  ERR(GRN_INVALID_ARGUMENT, "unsupported query");
892  GRN_FREE(dc);
893  return NULL;
894  }
895  dc->dat = dat;
896  return dc;
897 }
898 
899 grn_id
901 {
902  if (!c || !c->cursor) {
903  return GRN_ID_NIL;
904  }
905  try {
906  grn::dat::Cursor * const cursor = static_cast<grn::dat::Cursor *>(c->cursor);
907  const grn::dat::Key &key = cursor->next();
908  c->key = &key;
909  c->curr_rec = key.is_valid() ? key.id() : GRN_ID_NIL;
910  } catch (const grn::dat::Exception &ex) {
911  ERR(grn_dat_translate_error_code(ex.code()),
912  "grn::dat::Cursor::next failed");
913  return GRN_ID_NIL;
914  }
915  return c->curr_rec;
916 }
917 
918 void
920 {
921  if (c) {
922  grn_dat_cursor_fin(ctx, c);
923  GRN_FREE(c);
924  }
925 }
926 
927 int
928 grn_dat_cursor_get_key(grn_ctx *ctx, grn_dat_cursor *c, const void **key)
929 {
930  if (c) {
931  const grn::dat::Key &key_ref = *static_cast<const grn::dat::Key *>(c->key);
932  if (key_ref.is_valid()) {
933  *key = key_ref.ptr();
934  return (int)key_ref.length();
935  }
936  }
937  return 0;
938 }
939 
940 grn_rc
942  grn_table_delete_optarg *optarg)
943 {
944  if (!c || !c->cursor) {
945  return GRN_INVALID_ARGUMENT;
946  } else if (!grn_dat_open_trie_if_needed(ctx, c->dat)) {
947  return ctx->rc;
948  }
949  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(c->dat->trie);
950  if (!trie) {
951  return GRN_INVALID_ARGUMENT;
952  }
953  try {
954  if (trie->remove(c->curr_rec)) {
955  return GRN_SUCCESS;
956  }
957  } catch (const grn::dat::Exception &ex) {
958  ERR(grn_dat_translate_error_code(ex.code()),
959  "grn::dat::Trie::remove failed");
960  return GRN_INVALID_ARGUMENT;
961  }
962  return GRN_INVALID_ARGUMENT;
963 }
964 
965 grn_id
967 {
968  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
969  return GRN_ID_NIL;
970  }
971  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
972  if (trie) {
973  return trie->max_key_id();
974  }
975  return GRN_ID_NIL;
976 }
977 
978 grn_rc
980 {
981  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
982  return ctx->rc;
983  }
984  const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
985  if (!trie || !trie->max_key_id()) {
986  return GRN_SUCCESS;
987  }
988 
989  char trie_path[PATH_MAX];
990  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, dat->header->file_id + 1);
991  try {
992  grn::dat::Trie().create(trie_path);
993  } catch (const grn::dat::Exception &ex) {
994  const grn_rc error_code = grn_dat_translate_error_code(ex.code());
995  ERR(error_code, "grn::dat::Trie::create failed");
996  return error_code;
997  }
998  ++dat->header->file_id;
999  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1000  return ctx->rc;
1001  }
1002  return GRN_SUCCESS;
1003 }
1004 
1005 const char *
1006 _grn_dat_key(grn_ctx *ctx, grn_dat *dat, grn_id id, uint32_t *key_size)
1007 {
1008  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1009  return NULL;
1010  }
1011  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
1012  if (!trie) {
1013  return NULL;
1014  }
1015  const grn::dat::Key &key = trie->ith_key(id);
1016  if (!key.is_valid()) {
1017  return NULL;
1018  }
1019  *key_size = key.length();
1020  return static_cast<const char *>(key.ptr());
1021 }
1022 
1023 grn_id
1025 {
1026  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1027  return GRN_ID_NIL;
1028  }
1029  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
1030  if (!trie) {
1031  return GRN_ID_NIL;
1032  }
1033  while (id < trie->max_key_id()) {
1034  if (trie->ith_key(++id).is_valid()) {
1035  return id;
1036  }
1037  }
1038  return GRN_ID_NIL;
1039 }
1040 
1041 grn_id
1043 {
1044  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1045  return GRN_ID_NIL;
1046  }
1047  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
1048  if (!trie) {
1049  return GRN_ID_NIL;
1050  }
1051  const grn::dat::Key &key = trie->ith_key(id);
1052  if (!key.is_valid()) {
1053  return GRN_ID_NIL;
1054  }
1055  return id;
1056 }
1057 
1058 grn_rc
1060 {
1061  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1062  return ctx->rc;
1063  }
1064  grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
1065  if (!trie) {
1066  return GRN_INVALID_ARGUMENT;
1067  }
1068  trie->clear_status_flags();
1069  return GRN_SUCCESS;
1070 }
1071 
1072 grn_rc
1074 {
1075  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1076  return ctx->rc;
1077  }
1078  const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
1079  if (!trie) {
1080  return GRN_INVALID_ARGUMENT;
1081  }
1082 
1083  char trie_path[PATH_MAX];
1084  grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, dat->header->file_id + 1);
1085  try {
1086  grn::dat::Trie().repair(*trie, trie_path);
1087  } catch (const grn::dat::Exception &ex) {
1088  const grn_rc error_code = grn_dat_translate_error_code(ex.code());
1089  ERR(error_code, "grn::dat::Trie::create failed");
1090  return error_code;
1091  }
1092  ++dat->header->file_id;
1093  if (!grn_dat_open_trie_if_needed(ctx, dat)) {
1094  return ctx->rc;
1095  }
1096  return GRN_SUCCESS;
1097 }
1098 
1099 } // extern "C"