00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "catalog/pg_type.h"
00018 #include "executor/spi.h"
00019 #include "miscadmin.h"
00020 #include "tsearch/ts_utils.h"
00021 #include "utils/builtins.h"
00022
00023
00024 static int
00025 addone(int *counters, int last, int total)
00026 {
00027
00028 check_stack_depth();
00029
00030 counters[last]++;
00031 if (counters[last] >= total)
00032 {
00033 if (last == 0)
00034 return 0;
00035 if (addone(counters, last - 1, total - 1) == 0)
00036 return 0;
00037 counters[last] = counters[last - 1] + 1;
00038 }
00039 return 1;
00040 }
00041
00042
00043
00044
00045
00046 static QTNode *
00047 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
00048 {
00049
00050 if ((node->sign & ex->sign) != ex->sign ||
00051 node->valnode->type != ex->valnode->type)
00052 return node;
00053
00054 if (node->flags & QTN_NOCHANGE)
00055 return node;
00056
00057 if (node->valnode->type == QI_OPR)
00058 {
00059 if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
00060 return node;
00061
00062 if (node->nchild == ex->nchild)
00063 {
00064 if (QTNEq(node, ex))
00065 {
00066 QTNFree(node);
00067 if (subs)
00068 {
00069 node = QTNCopy(subs);
00070 node->flags |= QTN_NOCHANGE;
00071 }
00072 else
00073 node = NULL;
00074 *isfind = true;
00075 }
00076 }
00077 else if (node->nchild > ex->nchild)
00078 {
00079
00080
00081
00082
00083
00084 int *counters = (int *) palloc(sizeof(int) * node->nchild);
00085 int i;
00086 QTNode *tnode = (QTNode *) palloc(sizeof(QTNode));
00087
00088 memset(tnode, 0, sizeof(QTNode));
00089 tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
00090 tnode->nchild = ex->nchild;
00091 tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
00092 *(tnode->valnode) = *(ex->valnode);
00093
00094 for (i = 0; i < ex->nchild; i++)
00095 counters[i] = i;
00096
00097 do
00098 {
00099 tnode->sign = 0;
00100 for (i = 0; i < ex->nchild; i++)
00101 {
00102 tnode->child[i] = node->child[counters[i]];
00103 tnode->sign |= tnode->child[i]->sign;
00104 }
00105
00106 if (QTNEq(tnode, ex))
00107 {
00108 int j = 0;
00109
00110 pfree(tnode->valnode);
00111 pfree(tnode->child);
00112 pfree(tnode);
00113 if (subs)
00114 {
00115 tnode = QTNCopy(subs);
00116 tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
00117 }
00118 else
00119 tnode = NULL;
00120
00121 node->child[counters[0]] = tnode;
00122
00123 for (i = 1; i < ex->nchild; i++)
00124 node->child[counters[i]] = NULL;
00125 for (i = 0; i < node->nchild; i++)
00126 {
00127 if (node->child[i])
00128 {
00129 node->child[j] = node->child[i];
00130 j++;
00131 }
00132 }
00133
00134 node->nchild = j;
00135
00136 *isfind = true;
00137
00138 break;
00139 }
00140 } while (addone(counters, ex->nchild - 1, node->nchild));
00141 if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
00142 {
00143 pfree(tnode->valnode);
00144 pfree(tnode->child);
00145 pfree(tnode);
00146 }
00147 else
00148 QTNSort(node);
00149 pfree(counters);
00150 }
00151 }
00152 else
00153 {
00154 Assert(node->valnode->type == QI_VAL);
00155
00156 if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
00157 return node;
00158 else if (QTNEq(node, ex))
00159 {
00160 QTNFree(node);
00161 if (subs)
00162 {
00163 node = QTNCopy(subs);
00164 node->flags |= QTN_NOCHANGE;
00165 }
00166 else
00167 {
00168 node = NULL;
00169 }
00170 *isfind = true;
00171 }
00172 }
00173
00174 return node;
00175 }
00176
00177 static QTNode *
00178 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
00179 {
00180
00181 check_stack_depth();
00182
00183 root = findeq(root, ex, subs, isfind);
00184
00185 if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
00186 {
00187 int i;
00188
00189 for (i = 0; i < root->nchild; i++)
00190 root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
00191 }
00192
00193 return root;
00194 }
00195
00196 static QTNode *
00197 dropvoidsubtree(QTNode *root)
00198 {
00199
00200 if (!root)
00201 return NULL;
00202
00203 if (root->valnode->type == QI_OPR)
00204 {
00205 int i,
00206 j = 0;
00207
00208 for (i = 0; i < root->nchild; i++)
00209 {
00210 if (root->child[i])
00211 {
00212 root->child[j] = root->child[i];
00213 j++;
00214 }
00215 }
00216
00217 root->nchild = j;
00218
00219 if (root->nchild == 0)
00220 {
00221 QTNFree(root);
00222 root = NULL;
00223 }
00224 else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
00225 {
00226 QTNode *nroot = root->child[0];
00227
00228 pfree(root);
00229 root = nroot;
00230 }
00231 }
00232
00233 return root;
00234 }
00235
00236 QTNode *
00237 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
00238 {
00239 bool DidFind = false;
00240
00241 root = dofindsubquery(root, ex, subs, &DidFind);
00242
00243 if (!subs && DidFind)
00244 root = dropvoidsubtree(root);
00245
00246 if (isfind)
00247 *isfind = DidFind;
00248
00249 return root;
00250 }
00251
00252 Datum
00253 tsquery_rewrite_query(PG_FUNCTION_ARGS)
00254 {
00255 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
00256 text *in = PG_GETARG_TEXT_P(1);
00257 TSQuery rewrited = query;
00258 MemoryContext outercontext = CurrentMemoryContext;
00259 MemoryContext oldcontext;
00260 QTNode *tree;
00261 char *buf;
00262 SPIPlanPtr plan;
00263 Portal portal;
00264 bool isnull;
00265 int i;
00266
00267 if (query->size == 0)
00268 {
00269 PG_FREE_IF_COPY(in, 1);
00270 PG_RETURN_POINTER(rewrited);
00271 }
00272
00273 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
00274 QTNTernary(tree);
00275 QTNSort(tree);
00276
00277 buf = text_to_cstring(in);
00278
00279 SPI_connect();
00280
00281 if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
00282 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
00283
00284 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
00285 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
00286
00287 SPI_cursor_fetch(portal, true, 100);
00288
00289 if (SPI_tuptable == NULL ||
00290 SPI_tuptable->tupdesc->natts != 2 ||
00291 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
00292 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
00293 ereport(ERROR,
00294 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00295 errmsg("ts_rewrite query must return two tsquery columns")));
00296
00297 while (SPI_processed > 0 && tree)
00298 {
00299 for (i = 0; i < SPI_processed && tree; i++)
00300 {
00301 Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
00302 Datum sdata;
00303
00304 if (isnull)
00305 continue;
00306
00307 sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
00308
00309 if (!isnull)
00310 {
00311 TSQuery qtex = DatumGetTSQuery(qdata);
00312 TSQuery qtsubs = DatumGetTSQuery(sdata);
00313 QTNode *qex,
00314 *qsubs = NULL;
00315
00316 if (qtex->size == 0)
00317 {
00318 if (qtex != (TSQuery) DatumGetPointer(qdata))
00319 pfree(qtex);
00320 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
00321 pfree(qtsubs);
00322 continue;
00323 }
00324
00325 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
00326
00327 QTNTernary(qex);
00328 QTNSort(qex);
00329
00330 if (qtsubs->size)
00331 qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
00332
00333 oldcontext = MemoryContextSwitchTo(outercontext);
00334 tree = findsubquery(tree, qex, qsubs, NULL);
00335 MemoryContextSwitchTo(oldcontext);
00336
00337 QTNFree(qex);
00338 if (qtex != (TSQuery) DatumGetPointer(qdata))
00339 pfree(qtex);
00340 QTNFree(qsubs);
00341 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
00342 pfree(qtsubs);
00343
00344 if (tree)
00345 {
00346
00347 QTNClearFlags(tree, QTN_NOCHANGE);
00348 QTNSort(tree);
00349 }
00350 }
00351 }
00352
00353 SPI_freetuptable(SPI_tuptable);
00354 SPI_cursor_fetch(portal, true, 100);
00355 }
00356
00357 SPI_freetuptable(SPI_tuptable);
00358 SPI_cursor_close(portal);
00359 SPI_freeplan(plan);
00360 SPI_finish();
00361
00362 if (tree)
00363 {
00364 QTNBinary(tree);
00365 rewrited = QTN2QT(tree);
00366 QTNFree(tree);
00367 PG_FREE_IF_COPY(query, 0);
00368 }
00369 else
00370 {
00371 SET_VARSIZE(rewrited, HDRSIZETQ);
00372 rewrited->size = 0;
00373 }
00374
00375 pfree(buf);
00376 PG_FREE_IF_COPY(in, 1);
00377 PG_RETURN_POINTER(rewrited);
00378 }
00379
00380 Datum
00381 tsquery_rewrite(PG_FUNCTION_ARGS)
00382 {
00383 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
00384 TSQuery ex = PG_GETARG_TSQUERY(1);
00385 TSQuery subst = PG_GETARG_TSQUERY(2);
00386 TSQuery rewrited = query;
00387 QTNode *tree,
00388 *qex,
00389 *subs = NULL;
00390
00391 if (query->size == 0 || ex->size == 0)
00392 {
00393 PG_FREE_IF_COPY(ex, 1);
00394 PG_FREE_IF_COPY(subst, 2);
00395 PG_RETURN_POINTER(rewrited);
00396 }
00397
00398 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
00399 QTNTernary(tree);
00400 QTNSort(tree);
00401
00402 qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
00403 QTNTernary(qex);
00404 QTNSort(qex);
00405
00406 if (subst->size)
00407 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
00408
00409 tree = findsubquery(tree, qex, subs, NULL);
00410
00411 QTNFree(qex);
00412 QTNFree(subs);
00413
00414 if (!tree)
00415 {
00416 SET_VARSIZE(rewrited, HDRSIZETQ);
00417 rewrited->size = 0;
00418 PG_FREE_IF_COPY(ex, 1);
00419 PG_FREE_IF_COPY(subst, 2);
00420 PG_RETURN_POINTER(rewrited);
00421 }
00422 else
00423 {
00424 QTNBinary(tree);
00425 rewrited = QTN2QT(tree);
00426 QTNFree(tree);
00427 }
00428
00429 PG_FREE_IF_COPY(query, 0);
00430 PG_FREE_IF_COPY(ex, 1);
00431 PG_FREE_IF_COPY(subst, 2);
00432 PG_RETURN_POINTER(rewrited);
00433 }