ferret 0.10.4 → 0.10.5

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.
data/Rakefile CHANGED
@@ -112,7 +112,7 @@ EXT_SRC.each do |fn|
112
112
  mv dest_fn + ".out", dest_fn
113
113
  end
114
114
  end
115
- end
115
+ end if File.exists?("../c")
116
116
 
117
117
  desc "Build the extension"
118
118
  task :ext => ["ext/#{EXT}"] + SRC
@@ -843,9 +843,14 @@ static Token *std_next(TokenStream *ts)
843
843
  /* strip possesive */
844
844
  if ((t[-1] == 's' || t[-1] == 'S') && t[-2] == '\'') {
845
845
  t -= 2;
846
+ tk_set_ts(&(CTS(ts)->token), start, t, ts->text, 1);
847
+ CTS(ts)->token.end += 2;
848
+ }
849
+ else {
850
+ tk_set_ts(&(CTS(ts)->token), start, t, ts->text, 1);
846
851
  }
847
852
 
848
- return tk_set_ts(&(CTS(ts)->token), start, t, ts->text, 1);
853
+ return &(CTS(ts)->token);
849
854
  }
850
855
 
851
856
  if (*t == '&') { /* apostrophe case. */
@@ -1434,6 +1439,7 @@ Analyzer *per_field_analyzer_new(Analyzer *default_a)
1434
1439
 
1435
1440
  a->destroy_i = &pfa_destroy_i;
1436
1441
  a->get_ts = pfa_get_ts;
1442
+ a->ref_cnt = 1;
1437
1443
 
1438
1444
  return a;
1439
1445
  }
@@ -14,6 +14,7 @@ BitVector *bv_new_capa(int capa)
14
14
  bv->count = 0;
15
15
  bv->curr_bit = -1;
16
16
  bv->extends_as_ones = 0;
17
+ bv->ref_cnt = 1;
17
18
  return bv;
18
19
  }
19
20
 
@@ -24,8 +25,10 @@ BitVector *bv_new()
24
25
 
25
26
  void bv_destroy(BitVector * bv)
26
27
  {
27
- free(bv->bits);
28
- free(bv);
28
+ if (--(bv->ref_cnt) == 0) {
29
+ free(bv->bits);
30
+ free(bv);
31
+ }
29
32
  }
30
33
 
31
34
  void bv_set(BitVector * bv, int bit)
@@ -23,6 +23,7 @@ typedef struct BitVector
23
23
  int curr_bit;
24
24
 
25
25
  bool extends_as_ones : 1;
26
+ int ref_cnt;
26
27
  } BitVector;
27
28
 
28
29
  /**
@@ -10,10 +10,12 @@ static HashTable *object_map;
10
10
 
11
11
  /* IDs */
12
12
  ID id_new;
13
+ ID id_call;
14
+ ID id_eql;
15
+ ID id_hash;
13
16
  ID id_capacity;
14
17
  ID id_less_than;
15
18
  ID id_lt;
16
- ID id_call;
17
19
  ID id_is_directory;
18
20
  ID id_close;
19
21
  ID id_cclass;
@@ -36,6 +38,7 @@ VALUE mStringHelper;
36
38
  VALUE mSpans;
37
39
 
38
40
  /* Classes */
41
+ VALUE cTerm;
39
42
  /*
40
43
  */
41
44
 
@@ -98,13 +101,6 @@ VALUE frt_data_alloc(VALUE klass)
98
101
  return Frt_Make_Struct(klass);
99
102
  }
100
103
 
101
- VALUE frt_define_class_under(VALUE module, char *name, VALUE super)
102
- {
103
- VALUE klass = rb_define_class_under(module, name, super);
104
- rb_ivar_set(klass, id_cclass, Qtrue);
105
- return klass;
106
- }
107
-
108
104
  void frt_deref_free(void *p)
109
105
  {
110
106
  object_del(p);
@@ -233,6 +229,54 @@ void FRT_EXIT(const char *err_type, const char *fmt, ...)
233
229
  va_end(args);
234
230
  }
235
231
 
232
+ /****************************************************************************
233
+ *
234
+ * Term Methods
235
+ *
236
+ ****************************************************************************/
237
+ static ID id_field;
238
+ static ID id_text;
239
+
240
+ VALUE frt_get_term(const char *field, const char *text)
241
+ {
242
+ return rb_struct_new(cTerm,
243
+ ID2SYM(rb_intern(field)),
244
+ rb_str_new2(text),
245
+ NULL);
246
+ }
247
+
248
+ static VALUE frt_term_to_s(VALUE self)
249
+ {
250
+ VALUE rstr;
251
+ VALUE rfield = rb_funcall(self, id_field, 0);
252
+ VALUE rtext = rb_funcall(self, id_text, 0);
253
+ char *field = StringValuePtr(rfield);
254
+ char *text = StringValuePtr(rtext);
255
+ char *term_str = ALLOC_N(char,
256
+ 5 + RSTRING(rfield)->len + RSTRING(rtext)->len);
257
+ sprintf(term_str, "%s:%s", field, text);
258
+ rstr = rb_str_new2(term_str);
259
+ free(term_str);
260
+ return rstr;
261
+ }
262
+ /*
263
+ * Document-class: Ferret::Term
264
+ *
265
+ * == Summary
266
+ *
267
+ * A Term holds a term from a document and its field name (as a Symbol).
268
+ */
269
+ void Init_Term(void)
270
+ {
271
+ const char *term_class = "Term";
272
+ cTerm = rb_struct_define(term_class, "field", "text", NULL);
273
+ rb_set_class_path(cTerm, mFerret, term_class);
274
+ rb_const_set(mFerret, rb_intern(term_class), cTerm);
275
+ rb_define_method(cTerm, "to_s", frt_term_to_s, 0);
276
+ id_field = rb_intern("field");
277
+ id_text = rb_intern("text");
278
+ }
279
+
236
280
  /*
237
281
  * Document-module: Ferret
238
282
  *
@@ -241,6 +285,7 @@ void FRT_EXIT(const char *err_type, const char *fmt, ...)
241
285
  void Init_Ferret(void)
242
286
  {
243
287
  mFerret = rb_define_module("Ferret");
288
+ Init_Term();
244
289
  }
245
290
 
246
291
  void Init_ferret_ext(void)
@@ -254,6 +299,8 @@ void Init_ferret_ext(void)
254
299
  /* IDs */
255
300
  id_new = rb_intern("new");
256
301
  id_call = rb_intern("call");
302
+ id_eql = rb_intern("eql?");
303
+ id_hash = rb_intern("hash");
257
304
 
258
305
  id_capacity = rb_intern("capacity");
259
306
  id_less_than = rb_intern("less_than");
@@ -7,10 +7,12 @@
7
7
 
8
8
  /* IDs */
9
9
  extern ID id_new;
10
+ extern ID id_call;
11
+ extern ID id_hash;
12
+ extern ID id_eql;
10
13
  extern ID id_capacity;
11
14
  extern ID id_less_than;
12
15
  extern ID id_lt;
13
- extern ID id_call;
14
16
  extern ID id_is_directory;
15
17
  extern ID id_close;
16
18
  extern ID id_cclass;
@@ -35,6 +37,7 @@ extern VALUE mSpans;
35
37
  /* Classes */
36
38
  extern VALUE cDirectory;
37
39
  extern VALUE cLockError;
40
+ extern VALUE cTerm;
38
41
 
39
42
  /* Ferret Inits */
40
43
  extern void Init_Utils();
@@ -61,7 +64,7 @@ extern void frt_create_dir(VALUE rpath);
61
64
  extern VALUE frt_hs_to_rb_ary(HashSet *hs);
62
65
  extern void *frt_rb_data_ptr(VALUE val);
63
66
  extern char * frt_field(VALUE rfield);
64
- extern VALUE frt_define_class_under(VALUE module, char *name, VALUE super);
67
+ extern VALUE frt_get_term(const char *field, const char *term);
65
68
 
66
69
  #define Frt_Make_Struct(klass)\
67
70
  rb_data_object_alloc(klass,NULL,(RUBY_DATA_FUNC)NULL,(RUBY_DATA_FUNC)NULL)
@@ -81,3 +84,6 @@ extern VALUE frt_define_class_under(VALUE module, char *name, VALUE super);
81
84
  } while (0)
82
85
 
83
86
  #endif
87
+
88
+ #define frt_mark_cclass(klass) rb_ivar_set(klass, id_cclass, Qtrue)
89
+ #define frt_is_cclass(obj) (rb_ivar_get(CLASS_OF(obj), id_cclass) == Qtrue)
@@ -2899,10 +2899,9 @@ void ir_set_norm(IndexReader *ir, int doc_num, const char *field, uchar val)
2899
2899
  }
2900
2900
  }
2901
2901
 
2902
- uchar *ir_get_norms(IndexReader *ir, const char *field)
2902
+ uchar *ir_get_norms_i(IndexReader *ir, int field_num)
2903
2903
  {
2904
2904
  uchar *norms = NULL;
2905
- int field_num = fis_get_field_num(ir->fis, field);
2906
2905
  if (field_num >= 0) {
2907
2906
  norms = ir->get_norms(ir, field_num);
2908
2907
  }
@@ -2915,6 +2914,12 @@ uchar *ir_get_norms(IndexReader *ir, const char *field)
2915
2914
  return norms;
2916
2915
  }
2917
2916
 
2917
+ uchar *ir_get_norms(IndexReader *ir, const char *field)
2918
+ {
2919
+ int field_num = fis_get_field_num(ir->fis, field);
2920
+ return ir_get_norms_i(ir, field_num);
2921
+ }
2922
+
2918
2923
  uchar *ir_get_norms_into(IndexReader *ir, const char *field, uchar *buf)
2919
2924
  {
2920
2925
  int field_num = fis_get_field_num(ir->fis, field);
@@ -3286,6 +3291,7 @@ static BitVector *bv_read(Store *store, char *name)
3286
3291
  bv->size = (int)is_read_vint(is);
3287
3292
  bv->capa = (bv->size >> 5) + 1;
3288
3293
  bv->bits = ALLOC_AND_ZERO_N(f_u32, bv->capa);
3294
+ bv->ref_cnt = 1;
3289
3295
  for (i = (bv->size >> 5); i >= 0; i--) {
3290
3296
  bv->bits[i] = is_read_u32(is);
3291
3297
  }
@@ -5022,17 +5028,6 @@ int iw_doc_count(IndexWriter *iw)
5022
5028
  return doc_cnt;
5023
5029
  }
5024
5030
 
5025
- void iw_delete_term(IndexWriter *iw, const char *field, const char *term)
5026
- {
5027
- int field_num = fis_get_field_num(iw->fis, field);
5028
- if (field_num >= 0) {
5029
- DelTerm *dt = ALLOC(DelTerm);
5030
- dt->field_num = field_num;
5031
- dt->term = estrdup(term);
5032
- ary_push(iw->del_terms, dt);
5033
- }
5034
- }
5035
-
5036
5031
  static void delete_files(char **file_names, Store *store)
5037
5032
  {
5038
5033
  int i;
@@ -5277,37 +5272,9 @@ void iw_add_doc(IndexWriter *iw, Document *doc)
5277
5272
 
5278
5273
  static void iw_commit_i(IndexWriter *iw)
5279
5274
  {
5280
- /* optimized term deletion method */
5281
- const int del_term_cnt = ary_size(iw->del_terms);
5282
5275
  if (iw->dw && iw->dw->doc_num > 0) {
5283
5276
  iw_flush_ram_segment(iw);
5284
5277
  }
5285
- if (del_term_cnt) {
5286
- DelTerm **del_terms = iw->del_terms;
5287
- int i;
5288
- SegmentInfos *sis = iw->sis;
5289
- const int seg_cnt = sis->size;
5290
- for (i = 0; i < seg_cnt; i++) {
5291
- int j;
5292
- IndexReader *ir = sr_open(sis, iw->fis, i, false);
5293
- TermDocEnum *tde = ir->term_docs(ir);
5294
- for (j = 0; j < del_term_cnt; j++) {
5295
- DelTerm *del_term = del_terms[j];
5296
- stde_seek(tde, del_term->field_num, del_term->term);
5297
- while (tde->next(tde)) {
5298
- sr_delete_doc_i(ir, STDE(tde)->doc_num);
5299
- }
5300
- }
5301
- tde_destroy(tde);
5302
- sr_commit_i(ir);
5303
- ir_close(ir);
5304
- }
5305
- ary_each_rev(del_terms, i) {
5306
- free(del_terms[i]->term);
5307
- free(del_terms[i]);
5308
- }
5309
- ary_size(del_terms) = 0;
5310
- }
5311
5278
  }
5312
5279
 
5313
5280
  void iw_commit(IndexWriter *iw)
@@ -5317,6 +5284,32 @@ void iw_commit(IndexWriter *iw)
5317
5284
  mutex_unlock(&iw->mutex);
5318
5285
  }
5319
5286
 
5287
+ void iw_delete_term(IndexWriter *iw, const char *field, const char *term)
5288
+ {
5289
+ int field_num = fis_get_field_num(iw->fis, field);
5290
+ if (field_num >= 0) {
5291
+ int i;
5292
+ mutex_lock(&iw->mutex);
5293
+ iw_commit_i(iw);
5294
+ do {
5295
+ SegmentInfos *sis = iw->sis;
5296
+ const int seg_cnt = sis->size;
5297
+ for (i = 0; i < seg_cnt; i++) {
5298
+ IndexReader *ir = sr_open(sis, iw->fis, i, false);
5299
+ TermDocEnum *tde = ir->term_docs(ir);
5300
+ stde_seek(tde, field_num, term);
5301
+ while (tde->next(tde)) {
5302
+ sr_delete_doc_i(ir, STDE(tde)->doc_num);
5303
+ }
5304
+ tde_destroy(tde);
5305
+ sr_commit_i(ir);
5306
+ ir_close(ir);
5307
+ }
5308
+ } while (0);
5309
+ mutex_unlock(&iw->mutex);
5310
+ }
5311
+ }
5312
+
5320
5313
  static void iw_optimize_i(IndexWriter *iw)
5321
5314
  {
5322
5315
  int min_segment;
@@ -5351,7 +5344,6 @@ void iw_close(IndexWriter *iw)
5351
5344
  sis_destroy(iw->sis);
5352
5345
  fis_deref(iw->fis);
5353
5346
  sim_destroy(iw->similarity);
5354
- ary_free(iw->del_terms);
5355
5347
 
5356
5348
  iw->write_lock->release(iw->write_lock);
5357
5349
  iw->store->close_lock(iw->write_lock);
@@ -5392,7 +5384,6 @@ IndexWriter *iw_open(Store *store, Analyzer *analyzer, const Config *config)
5392
5384
  XENDTRY
5393
5385
 
5394
5386
  iw->similarity = sim_create_default();
5395
- iw->del_terms = ary_new_type(DelTerm *);
5396
5387
  iw->analyzer = analyzer ? analyzer : mb_standard_analyzer_new(true);
5397
5388
 
5398
5389
  REF(store);
@@ -744,6 +744,7 @@ extern void ir_undelete_all(IndexReader *ir);
744
744
  extern int ir_doc_freq(IndexReader *ir, const char *field, const char *term);
745
745
  extern void ir_set_norm(IndexReader *ir, int doc_num, const char *field,
746
746
  uchar val);
747
+ extern uchar *ir_get_norms_i(IndexReader *ir, int field_num);
747
748
  extern uchar *ir_get_norms(IndexReader *ir, const char *field);
748
749
  extern uchar *ir_get_norms_into(IndexReader *ir, const char *field, uchar *buf);
749
750
  extern void ir_destroy(IndexReader *self);
@@ -868,7 +869,6 @@ struct IndexWriter
868
869
  FieldInfos *fis;
869
870
  DocWriter *dw;
870
871
  Similarity *similarity;
871
- DelTerm **del_terms;
872
872
  Lock *write_lock;
873
873
  };
874
874
 
@@ -388,7 +388,7 @@ static bool csc_skip_to(Scorer *self, int doc_num)
388
388
  csc_sort_scorers(csc);
389
389
  }
390
390
 
391
- more = csc->more;
391
+ csc->more = more;
392
392
  return csc_do_next(self);
393
393
  }
394
394
 
@@ -357,7 +357,7 @@ static Scorer *multi_tw_scorer(Weight *self, IndexReader *ir)
357
357
  te->close(te);
358
358
  if (tdew_cnt) {
359
359
  multi_tsc = multi_tsc_new(self, MTQ(self->query)->field, tdew_a,
360
- tdew_cnt, ir->get_norms(ir, field_num));
360
+ tdew_cnt, ir_get_norms_i(ir, field_num));
361
361
  }
362
362
  else {
363
363
  free(tdew_a);
@@ -559,6 +559,17 @@ static void multi_tq_destroy_i(Query *self)
559
559
  q_destroy_i(self);
560
560
  }
561
561
 
562
+ static void multi_tq_extract_terms(Query *self, HashSet *terms)
563
+ {
564
+ int i;
565
+ char *field = MTQ(self)->field;
566
+ PriorityQueue *boosted_terms = MTQ(self)->boosted_terms;
567
+ for (i = boosted_terms->size; i > 0; i--) {
568
+ BoostedTerm *bt = (BoostedTerm *)boosted_terms->heap[i];
569
+ hs_add(terms, term_new(field, bt->term));
570
+ }
571
+ }
572
+
562
573
  static ulong multi_tq_hash(Query *self)
563
574
  {
564
575
  int i;
@@ -631,6 +642,7 @@ Query *multi_tq_new_conf(const char *field, int max_terms, float min_boost)
631
642
 
632
643
  self->type = MULTI_TERM_QUERY;
633
644
  self->to_s = &multi_tq_to_s;
645
+ self->extract_terms = &multi_tq_extract_terms;
634
646
  self->hash = &multi_tq_hash;
635
647
  self->eq = &multi_tq_eq;
636
648
  self->destroy_i = &multi_tq_destroy_i;
@@ -170,8 +170,8 @@ static Phrase *ph_first_word(char *word);
170
170
  static Phrase *ph_add_word(Phrase *self, char *word);
171
171
  static Phrase *ph_add_multi_word(Phrase *self, char *word);
172
172
 
173
- static Query *get_range_q(const char *field, const char *from, const char *to,
174
- bool inc_lower, bool inc_upper);
173
+ static Query *get_r_q(QParser *qp, char *field, char *from, char *to,
174
+ bool inc_lower, bool inc_upper);
175
175
 
176
176
  #define FLDS(q, func) do {\
177
177
  char *field;\
@@ -1405,62 +1405,62 @@ yyreduce:
1405
1405
 
1406
1406
  case 39:
1407
1407
  #line 146 "src/q_parser.y"
1408
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-2].str), (yyvsp[-1].str), true, true)); }
1408
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-2].str), (yyvsp[-1].str), true, true)); }
1409
1409
  break;
1410
1410
 
1411
1411
  case 40:
1412
1412
  #line 147 "src/q_parser.y"
1413
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-2].str), (yyvsp[-1].str), true, false)); }
1413
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-2].str), (yyvsp[-1].str), true, false)); }
1414
1414
  break;
1415
1415
 
1416
1416
  case 41:
1417
1417
  #line 148 "src/q_parser.y"
1418
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-2].str), (yyvsp[-1].str), false, true)); }
1418
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-2].str), (yyvsp[-1].str), false, true)); }
1419
1419
  break;
1420
1420
 
1421
1421
  case 42:
1422
1422
  #line 149 "src/q_parser.y"
1423
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-2].str), (yyvsp[-1].str), false, false)); }
1423
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-2].str), (yyvsp[-1].str), false, false)); }
1424
1424
  break;
1425
1425
 
1426
1426
  case 43:
1427
1427
  #line 150 "src/q_parser.y"
1428
- { FLDS((yyval.query), get_range_q(field, NULL,(yyvsp[-1].str), false, false)); }
1428
+ { FLDS((yyval.query), get_r_q(qp, field, NULL,(yyvsp[-1].str), false, false)); }
1429
1429
  break;
1430
1430
 
1431
1431
  case 44:
1432
1432
  #line 151 "src/q_parser.y"
1433
- { FLDS((yyval.query), get_range_q(field, NULL,(yyvsp[-1].str), false, true)); }
1433
+ { FLDS((yyval.query), get_r_q(qp, field, NULL,(yyvsp[-1].str), false, true)); }
1434
1434
  break;
1435
1435
 
1436
1436
  case 45:
1437
1437
  #line 152 "src/q_parser.y"
1438
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-1].str), NULL,true, false)); }
1438
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-1].str), NULL,true, false)); }
1439
1439
  break;
1440
1440
 
1441
1441
  case 46:
1442
1442
  #line 153 "src/q_parser.y"
1443
- { FLDS((yyval.query), get_range_q(field, (yyvsp[-1].str), NULL,false, false)); }
1443
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[-1].str), NULL,false, false)); }
1444
1444
  break;
1445
1445
 
1446
1446
  case 47:
1447
1447
  #line 154 "src/q_parser.y"
1448
- { FLDS((yyval.query), get_range_q(field, NULL,(yyvsp[0].str), false, false)); }
1448
+ { FLDS((yyval.query), get_r_q(qp, field, NULL,(yyvsp[0].str), false, false)); }
1449
1449
  break;
1450
1450
 
1451
1451
  case 48:
1452
1452
  #line 155 "src/q_parser.y"
1453
- { FLDS((yyval.query), get_range_q(field, NULL,(yyvsp[0].str), false, true)); }
1453
+ { FLDS((yyval.query), get_r_q(qp, field, NULL,(yyvsp[0].str), false, true)); }
1454
1454
  break;
1455
1455
 
1456
1456
  case 49:
1457
1457
  #line 156 "src/q_parser.y"
1458
- { FLDS((yyval.query), get_range_q(field, (yyvsp[0].str), NULL,true, false)); }
1458
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[0].str), NULL,true, false)); }
1459
1459
  break;
1460
1460
 
1461
1461
  case 50:
1462
1462
  #line 157 "src/q_parser.y"
1463
- { FLDS((yyval.query), get_range_q(field, (yyvsp[0].str), NULL,false, false)); }
1463
+ { FLDS((yyval.query), get_r_q(qp, field, (yyvsp[0].str), NULL,false, false)); }
1464
1464
  break;
1465
1465
 
1466
1466
 
@@ -1756,7 +1756,7 @@ static int get_word(YYSTYPE *lvalp, QParser *qp)
1756
1756
  while (!strchr(not_word, (c=*qp->qstrp++))) {
1757
1757
  switch (c) {
1758
1758
  case '\\':
1759
- if ((c=*qp->qstrp) == ' ' && c != '\t' && c != '\0') {
1759
+ if ((c=*qp->qstrp) == '\0') {
1760
1760
  *bufp++ = '\\';
1761
1761
  }
1762
1762
  else {
@@ -2248,10 +2248,25 @@ static Query *get_phrase_q(QParser *qp, Phrase *phrase, char *slop_str)
2248
2248
  return q;
2249
2249
  }
2250
2250
 
2251
- static Query *get_range_q(const char *field, const char *from, const char *to,
2252
- bool inc_lower, bool inc_upper)
2251
+ static Query *get_r_q(QParser *qp, char *field, char *from, char *to,
2252
+ bool inc_lower, bool inc_upper)
2253
2253
  {
2254
- return rq_new(field, from, to, inc_lower, inc_upper);
2254
+ Query *rq;
2255
+ if (from) {
2256
+ TokenStream *stream = get_cached_ts(qp, field, from);
2257
+ Token *token = ts_next(stream);
2258
+ from = token ? estrdup(token->text) : NULL;
2259
+ }
2260
+ if (to) {
2261
+ TokenStream *stream = get_cached_ts(qp, field, to);
2262
+ Token *token = ts_next(stream);
2263
+ to = token ? estrdup(token->text) : NULL;
2264
+ }
2265
+
2266
+ rq = rq_new(field, from, to, inc_lower, inc_upper);
2267
+ if (from) free(from);
2268
+ if (to) free(to);
2269
+ return rq;
2255
2270
  }
2256
2271
 
2257
2272
  void qp_destroy(QParser *self)