rpm 5.3.12
rpmio/rpmsql.c
Go to the documentation of this file.
00001 #include "system.h"
00002 
00003 #include <popt.h>
00004 
00005 #define _RPMIOB_INTERNAL        /* rpmiobSlurp */
00006 #include "rpmio_internal.h"     /* XXX fdGetFILE */
00007 #include <rpmmacro.h>
00008 #include <rpmdir.h>
00009 #include <rpmurl.h>
00010 #include <mire.h>
00011 
00012 #if defined(WITH_DBSQL)
00013 #include <db51/dbsql.h>
00014 #elif defined(WITH_SQLITE)
00015 #define SQLITE_OS_UNIX 1
00016 #define SQLITE_THREADSAFE 1
00017 #define SQLITE_THREAD_OVERRIDE_LOCK -1
00018 #define SQLITE_TEMP_STORE 1
00019 #include <sqlite3.h>
00020 #endif  /* WITH_SQLITE */
00021 
00022 #define _RPMSQL_INTERNAL
00023 #define _RPMVT_INTERNAL
00024 #define _RPMVC_INTERNAL
00025 #include <rpmsql.h>
00026 
00027 #ifdef  NOTYET          /* XXX FIXME */
00028 #include <editline/readline.h>
00029 #elif defined(HAVE_READLINE) && HAVE_READLINE==1
00030 # include <readline/readline.h>
00031 # include <readline/history.h>
00032 #endif
00033 
00034 # define readline(sql, p) local_getline(sql, p)
00035 # define add_history(X)
00036 # define read_history(X)
00037 # define write_history(X)
00038 # define stifle_history(X)
00039 
00040 #include "debug.h"
00041 
00042 /*@unchecked@*/
00043 int _rpmsql_debug = 0;
00044 
00045 /*@unchecked@*/
00046 int _rpmvt_debug = 0;
00047 
00048 /*@unchecked@*/
00049 int _rpmvc_debug = 0;
00050 
00051 /*@unchecked@*/ /*@relnull@*/
00052 rpmsql _rpmsqlI = NULL;
00053 
00054 /*@unchecked@*/
00055 volatile int _rpmsqlSeenInterrupt;
00056 
00057 #if defined(WITH_SQLITE)
00058 /*@unchecked@*/
00059 static struct rpmsql_s _sql;
00060 #endif /* defined(WITH_SQLITE) */
00061 
00062 /*==============================================================*/
00063 
00064 #define VTDBG(_vt, _l) if ((_vt)->debug) fprintf _l
00065 #define VTDBGNOISY(_vt, _l) if ((_vt)->debug < 0) fprintf _l
00066 
00070 static void rpmvtFini(void * _VT)
00071         /*@globals fileSystem @*/
00072         /*@modifies *_VT, fileSystem @*/
00073 {
00074     struct rpmVT_s * VT = _VT;
00075     rpmvt vt = &VT->vt;
00076     
00077 
00078 VTDBGNOISY(vt, (stderr, "==> %s(%p)\n", __FUNCTION__, vt));
00079     vt->argv = argvFree(vt->argv);
00080     vt->argc = 0;
00081     vt->fields = argvFree(vt->fields);
00082     vt->nfields = 0;
00083     vt->cols = argvFree(vt->cols);
00084     vt->ncols = 0;
00085     vt->av = argvFree(vt->av);
00086     vt->ac = 0;
00087 }
00088 
00089 /*@unchecked@*/ /*@only@*/ /*@null@*/
00090 rpmioPool _rpmvtPool;
00091 
00092 static rpmvt rpmvtGetPool(/*@null@*/ rpmioPool pool)
00093         /*@globals _rpmvtPool, fileSystem @*/
00094         /*@modifies pool, _rpmvtPool, fileSystem @*/
00095 {
00096     struct rpmVT_s * VT;
00097 
00098     if (_rpmvtPool == NULL) {
00099         _rpmvtPool = rpmioNewPool("vt", sizeof(*VT), -1, _rpmvt_debug,
00100                         NULL, NULL, rpmvtFini);
00101         pool = _rpmvtPool;
00102     }
00103     VT = (struct rpmVT_s *) rpmioGetPool(pool, sizeof(*VT));
00104     memset(((char *)VT)+sizeof(VT->_item), 0, sizeof(*VT)-sizeof(VT->_item));
00105     return &VT->vt;
00106 }
00107 
00108 rpmvt rpmvtNew(void * db, void * pModule, const char *const * argv, rpmvd vd)
00109 {
00110     rpmvt vt = rpmvtLink(rpmvtGetPool(_rpmvtPool));
00111 
00112     vt->db = db;
00113     (void) argvAppend(&vt->argv, (ARGV_t) argv);
00114     vt->argc = argvCount(vt->argv);
00115     if (vd->split && vd->parse && *vd->parse) {
00116         char * parse = rpmExpand(vd->parse, NULL);
00117         int xx;
00118         xx = argvSplit(&vt->fields, parse, vd->split);
00119 assert(xx == 0);
00120         vt->nfields = argvCount(vt->fields);
00121         parse = _free(parse);
00122     }
00123 
00124     vt->av = NULL;
00125     vt->ac = 0;
00126 
00127     vt->vd = vd;
00128     vt->debug = _rpmvt_debug;
00129 
00130 VTDBG(vt, (stderr, "\tdbpath: %s\n", vd->dbpath));
00131 VTDBG(vt, (stderr, "\tprefix: %s\n", vd->prefix));
00132 VTDBG(vt, (stderr, "\t split: %s\n", vd->split));
00133 VTDBG(vt, (stderr, "\t parse: %s\n", vd->parse));
00134 VTDBG(vt, (stderr, "\t regex: %s\n", vd->regex));
00135 
00136     return vt;
00137 }
00138 
00139 /*==============================================================*/
00140 
00141 #if defined(WITH_SQLITE)
00142 
00143 typedef struct key_s {
00144     const char * k;
00145     uint32_t v;
00146 } KEY;
00147 static KEY sqlTypes[] = {
00148     { "blob",   SQLITE_BLOB },
00149     { "float",  SQLITE_FLOAT },
00150     { "int",    SQLITE_INTEGER },
00151     { "integer",SQLITE_INTEGER },
00152     { "null",   SQLITE_NULL },
00153     { "text",   SQLITE_TEXT },
00154 };
00155 static size_t nsqlTypes = sizeof(sqlTypes) / sizeof(sqlTypes[0]);
00156 
00157 static const char * hasSqlType(const char * s)
00158 {
00159     int i;
00160     for (i = 0; i < (int)nsqlTypes; i++) {
00161         const char * k = sqlTypes[i].k;
00162         const char * se = strcasestr(s, k);
00163         if (se == NULL || se <= s || se[-1] != ' ')
00164             continue;
00165         se += strlen(k);
00166         if (*se && *se != ' ')
00167             continue;
00168         return se;
00169     }
00170     return NULL;
00171 }
00172 
00173 static char * _rpmvtJoin(const char * a, const char ** argv, const char * z)
00174 {
00175     static const char _type[] = " TEXT";
00176     const char ** av;
00177     size_t na = (sizeof("\t")-1) + (a ? strlen(a) : 0);
00178     size_t nb = 0;
00179     size_t nz = (z ? strlen(z) : 0) + strlen(_type) + (sizeof(",\n")-1);
00180     char *t, *te;
00181 
00182     for (av = argv; *av != NULL; av++)
00183         nb += na + strlen(*av) + nz;
00184 
00185     te = t = xmalloc(nb + 1);
00186     for (av = argv; *av != NULL; av++) {
00187         *te++ = '\t';
00188         te = stpcpy(te, a);
00189         te = stpcpy(te, *av);
00190         if (hasSqlType(*av) == NULL)
00191             te = stpcpy(te, _type);
00192         te = stpcpy(te, z);
00193         *te++ = ',';
00194         *te++ = '\n';
00195     }
00196     *te = '\0';
00197 
00198     return t;
00199 }
00200 
00201 static char * _rpmvtAppendCols(rpmvt vt, const char ** av)
00202 {
00203     char * h = _rpmvtJoin("", av, "");
00204     int xx = argvAppend(&vt->cols, av);
00205     char * u;
00206     char * hu;
00207     /* XXX permit user column overrides w/o a argv[3] selector. */
00208     rpmvd vd = vt->vd;
00209     int fx = (vd->fx == 3 ? 3 : 4);
00210 
00211     av = (const char **) (vt->argc > fx ? &vt->argv[fx] : vt->fields);
00212 assert(av);
00213     u = _rpmvtJoin("", av, "");
00214     u[strlen(u)-2] = ' ';       /* XXX nuke the final comma */
00215     xx = argvAppend(&vt->cols, av);
00216 
00217 #define dbN     vt->argv[1]
00218 #define tblN    vt->argv[2]
00219     hu = rpmExpand("CREATE TABLE ", dbN, ".", tblN, " (\n", h, u, ");", NULL);
00220 #undef  dbN
00221 #undef  tblN
00222 
00223     u = _free(u);
00224     h = _free(h);
00225 
00226 VTDBG(vt, (stderr, "%s\n", hu));
00227     return hu;
00228 }
00229 
00230 int rpmvtLoadArgv(rpmvt vt, rpmvt * vtp)
00231 {
00232     sqlite3 * db = (sqlite3 *) vt->db;
00233     rpmvd vd = vt->vd;
00234 
00235     static const char * hidden[] = { "path HIDDEN", "id HIDDEN", NULL };
00236     const char * hu;
00237 
00238     char * uri = NULL;
00239     struct stat sb;
00240 
00241     const char * fn = NULL;
00242 
00243     int rc = SQLITE_OK;
00244     int xx;
00245 
00246 VTDBG(vt, (stderr, "--> %s(%p,%p)\n", __FUNCTION__, vt, vtp));
00247 if (vt->debug)
00248 argvPrint("vt->argv", (ARGV_t)vt->argv, NULL);
00249 
00250     /* Set the columns in the schema. */
00251     hu = _rpmvtAppendCols(vt, hidden);
00252     rc = rpmsqlCmd(NULL, "declare_vtab", db,
00253                         sqlite3_declare_vtab(db, hu));
00254     hu = _free(hu);
00255 
00256     if (vt->argv[3]) {
00257         /* XXX slice out the quotes that sqlite passes through ...  */
00258         static char _quotes[] = "'\"";
00259         int quoted = (strchr(_quotes, *vt->argv[3]) != NULL);
00260         const char * prefix;
00261         const char * path = NULL;
00262         /* XXX Prefer user override to global prefix (if absolute path). */
00263         (void) urlPath(vt->argv[3]+quoted, &path);
00264         prefix = (*path != '/' && vd->prefix ? vd->prefix : "");
00265         uri = rpmGetPath(prefix, path, NULL);
00266         uri[strlen(uri)-quoted] = '\0';
00267     } else
00268         uri = rpmGetPath(vd->prefix, fn, NULL);
00269 
00270     (void) urlPath(uri, (const char **) &fn);
00271 
00272     if (!strcasecmp(vt->argv[0], "nixdb")) {
00273         const char * out = rpmExpand("%{sql ", vd->dbpath, ":",
00274             "select path from ValidPaths where glob('", fn, "', path);",
00275                 "}", NULL);
00276         (void) argvSplit(&vt->av, out, "\n");
00277         out = _free(out);
00278     } else
00279 
00280     if (!strcasecmp(vt->argv[0], "Env")) {
00281         int fx = 4;     /* XXX user column overrides? */
00282 if (vt->debug)
00283 fprintf(stderr, " ENV: getenv(%p[%d])\n", &vt->argv[fx], argvCount(&vt->argv[fx]));
00284         /* XXX permit glob selector filtering from argv[3]? */
00285         xx = argvAppend(&vt->av, (ARGV_t)environ);
00286     } else
00287 
00288     if (fn[0] == '/') {
00289 if (vt->debug)
00290 fprintf(stderr, "*** uri %s fn %s\n", uri, fn);
00291         if (Glob_pattern_p(uri, 0)) {   /* XXX uri */
00292             const char ** av = NULL;
00293             int ac = 0;
00294                 
00295             xx = rpmGlob(uri, &ac, &av);        /* XXX uri */
00296 if (vt->debug)
00297 fprintf(stderr, "GLOB: %d = Glob(%s) av %p[%d]\n", xx, uri, av, ac);
00298             if (xx)
00299                 rc = SQLITE_NOTFOUND;           /* XXX */
00300             else
00301                 xx = argvAppend(&vt->av, (ARGV_t)av);
00302             av = argvFree(av);
00303         } else
00304         if (uri[strlen(uri)-1] == '/') {
00305             DIR * dir = Opendir(uri);
00306             struct dirent * dp;
00307 if (vt->debug)
00308 fprintf(stderr, " DIR: %p = Opendir(%s)\n", dir, uri);
00309             if (dir == NULL)
00310                 rc = SQLITE_NOTFOUND;           /* XXX */
00311             else
00312             while ((dp = Readdir(dir)) != NULL) {
00313                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
00314                     continue;
00315                 fn = rpmGetPath(uri, "/", dp->d_name, NULL);
00316                 xx = argvAdd(&vt->av, fn);
00317                 fn = _free(fn);
00318             }
00319             if (dir) xx = Closedir(dir);
00320         } else
00321         if (!Lstat(uri, &sb)) {
00322             rpmiob iob = NULL;
00323             xx = rpmiobSlurp(uri, &iob);
00324 if (vt->debug)
00325 fprintf(stderr, "FILE: %d = Slurp(%s)\n", xx, uri);
00326             if (!xx)
00327                 xx = argvSplit(&vt->av, rpmiobStr(iob), "\n");
00328             else
00329                 rc = SQLITE_NOTFOUND;           /* XXX */
00330             iob = rpmiobFree(iob);
00331         } else
00332             rc = SQLITE_NOTFOUND;               /* XXX */
00333     } else {
00334         xx = argvAppend(&vt->av, (ARGV_t)&vt->argv[3]);
00335 if (vt->debug)
00336 fprintf(stderr, "LIST: %d = Append(%p[%d])\n", xx, &vt->argv[3], argvCount(&vt->argv[3]));
00337     }
00338 
00339     vt->ac = argvCount((ARGV_t)vt->av);
00340 
00341     uri = _free(uri);
00342 
00343 if (vt->debug)
00344 argvPrint("vt->av", (ARGV_t)vt->av, NULL);
00345 
00346     if (vtp) {
00347         if (!rc)
00348             *vtp = (rpmvt) vt;
00349         else {
00350             *vtp = NULL;
00351             (void) rpmvtFree(vt);
00352             vt = NULL;
00353         }
00354     } else {
00355         vt = rpmvtFree(vt);
00356         vt = NULL;
00357     }
00358 
00359 VTDBG(vt, (stderr, "<-- %s(%p,%p) rc %d\n", __FUNCTION__, vt, vtp, rc));
00360 
00361     return rc;
00362 }
00363 
00364 /*==============================================================*/
00365 
00366 static struct rpmvd_s _argVD = {
00367 };
00368 
00369 int rpmvtCreate(void * _db, void * pAux,
00370                 int argc, const char *const * argv,
00371                 rpmvt * vtp, char ** pzErr)
00372 {
00373     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_argVD), vtp);
00374 }
00375 
00376 int rpmvtConnect(void * _db, void * pAux,
00377                 int argc, const char *const * argv,
00378                 rpmvt * vtp, char ** pzErr)
00379 {
00380     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_argVD), vtp);
00381 }
00382 
00383 #ifdef  NOTYET
00384 static void dumpInfo(const char * msg, const struct sqlite3_index_info * s)
00385 {
00386 fprintf(stderr, "--------------------- %s\n", (msg ? msg : ""));
00387 #define _PRT(f,v) fprintf(stderr, "%20s: " #f "\n", #v, s->v)
00388         _PRT(%p, aConstraintUsage);
00389         _PRT(%d, idxNum);
00390         _PRT(%s, idxStr);
00391         _PRT(%d, needToFreeIdxStr);
00392         _PRT(%d, orderByConsumed);
00393         _PRT(%g, estimatedCost);
00394 #undef  _PRT
00395 }
00396 #endif
00397 
00398 int rpmvtBestIndex(rpmvt vt, void * _pInfo)
00399 {
00400     sqlite3_index_info * pInfo = (sqlite3_index_info *) _pInfo;
00401     int rc = SQLITE_OK;
00402 #ifdef  NOTYET
00403     int i;
00404 #endif
00405 
00406 VTDBG(vt, (stderr, "--> %s(%p,%p)\n", __FUNCTION__, vt, pInfo));
00407 
00408 #ifdef  NOTYET
00409     if (pInfo->aConstraint)
00410     for (i = 0; i < pInfo->nConstraint; i++) {
00411         const struct sqlite3_index_constraint * p = pInfo->aConstraint + i;
00412         fprintf(stderr, "\tcol %s(%d) 0x%02x 0x%02x\n", vt->cols[p->iColumn], p->iColumn,
00413                 p->op, p->usable);
00414     }
00415     if (pInfo->aOrderBy)
00416     for (i = 0; i < pInfo->nOrderBy; i++) {
00417         const struct sqlite3_index_orderby * p = pInfo->aOrderBy + i;
00418         fprintf(stderr, "\tcol %s(%d) %s\n", vt->cols[p->iColumn], p->iColumn,
00419                 (p->desc ? "DESC" : "ASC"));
00420     }
00421     dumpInfo(__FUNCTION__, pInfo);
00422 #endif
00423 
00424 VTDBG(vt, (stderr, "<-- %s(%p,%p) rc %d\n", __FUNCTION__, vt, pInfo, rc));
00425 
00426     return rc;
00427 }
00428 
00429 int rpmvtDisconnect(rpmvt vt)
00430 {
00431     (void) rpmvtFree(vt);
00432     return 0;   /* SQLITE_OK */
00433 }
00434 
00435 int rpmvtDestroy(rpmvt vt)
00436 {
00437     (void) rpmvtFree(vt);
00438     return 0;   /* SQLITE_OK */
00439 }
00440 
00441 static const char * dumpArg(rpmvArg _v)
00442 {
00443     static char buf[BUFSIZ];
00444     char * b = buf;
00445     size_t nb = sizeof(buf);
00446     sqlite3_value * v = (sqlite3_value *) _v;
00447     int vtype = sqlite3_value_type(v);
00448     unsigned long long ll;
00449     double d;
00450     const void * p;
00451     const char * s;
00452 
00453     snprintf(b, nb, "%p(%d)", v, vtype);
00454     nb -= strlen(b);
00455     b += strlen(b);
00456 
00457     switch (vtype) {
00458     case SQLITE_INTEGER:
00459         ll = (unsigned long long) sqlite3_value_int64(v);
00460         snprintf(b, nb, " INT  %lld", ll);
00461         break;
00462     case SQLITE_FLOAT:
00463         d = sqlite3_value_double(v);
00464         snprintf(b, nb, " REAL %g", d);
00465         break;
00466     case SQLITE_BLOB:
00467         p = sqlite3_value_blob(v);
00468         snprintf(b, nb, " BLOB %p", p);
00469         break;
00470     case SQLITE_NULL:
00471         snprintf(b, nb, " NULL");
00472         break;
00473     case SQLITE_TEXT:
00474         s = (const char *)sqlite3_value_text(v);
00475         snprintf(b, nb, " TEXT \"%s\"", s);
00476         break;
00477     default:
00478         snprintf(b, nb, " UNKNOWN");
00479         break;
00480     }
00481 
00482     return buf;
00483 }
00484 
00485 static void dumpArgv(const char * msg, int argc, rpmvArg * _argv)
00486 {
00487     if (argc > 0 && _argv) {
00488         int i;
00489         fprintf(stderr, "--------------------- %s\n", (msg ? msg : ""));
00490         for (i = 0; i < argc; i++)
00491             fprintf(stderr, "\targv[%d] %s\n", i, dumpArg(_argv[i]));
00492     }
00493 }
00494 
00495 int rpmvtUpdate(rpmvt vt, int argc, rpmvArg * _argv, int64_t * pRowid)
00496 {
00497     sqlite3_value ** argv = (sqlite3_value **) _argv;
00498     int rc = SQLITE_OK;
00499 
00500 VTDBG(vt, (stderr, "--> %s(%p,%p[%u],%p)\n", __FUNCTION__, vt, argv, (unsigned)argc, pRowid));
00501 
00502     if (argc == 0 || argv == NULL) {
00503 if (vt->debug)
00504 dumpArgv("ERROR", argc, _argv);
00505         rc = SQLITE_NOTFOUND;   /* XXX */
00506     } else
00507     if (argc == 1) {
00508 VTDBG(vt, (stderr, "\tDELETE ROW 0x%llx\n", *(unsigned long long *)argv[0]));
00509     } else
00510     if (argv[0] == NULL) {
00511 VTDBG(vt, (stderr, "\tADD ROW 0x%llx\n", *(unsigned long long *)argv[1]));
00512 if (vt->debug)
00513 dumpArgv("ADD ROW", argc, _argv);
00514     } else
00515     if (argv[0] == argv[1]) {
00516 VTDBG(vt, (stderr, "\tUPDATE ROW 0x%llx\n", *(unsigned long long *)argv[1]));
00517 if (vt->debug)
00518 dumpArgv("UPDATE argv", argc-2, _argv+2);
00519     } else {
00520 VTDBG(vt, (stderr, "\tREPLACE ROW 0x%llx from 0x%llx\n",
00521                 *(unsigned long long *)argv[0], *(unsigned long long *)argv[1]));
00522 if (vt->debug)
00523 dumpArgv("REPLACE argv", argc-2, _argv+2);
00524     }
00525 
00526 VTDBG(vt, (stderr, "<-- %s(%p,%p[%u],%p) rc %d\n", __FUNCTION__, vt, argv, (unsigned)argc, pRowid, rc));
00527     return rc;
00528 }
00529 
00530 int rpmvtBegin(rpmvt vt)
00531 {
00532     int rc = SQLITE_OK;
00533 VTDBG(vt, (stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, vt, rc));
00534     return rc;
00535 }
00536 
00537 int rpmvtSync(rpmvt vt)
00538 {
00539     int rc = SQLITE_OK;
00540 VTDBG(vt, (stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, vt, rc));
00541     return rc;
00542 }
00543 
00544 int rpmvtCommit(rpmvt vt)
00545 {
00546     int rc = SQLITE_OK;
00547 VTDBG(vt, (stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, vt, rc));
00548     return rc;
00549 }
00550 
00551 int rpmvtRollback(rpmvt vt)
00552 {
00553     int rc = SQLITE_OK;
00554 VTDBG(vt, (stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, vt, rc));
00555     return rc;
00556 }
00557 
00558 int rpmvtFindFunction(rpmvt vt, int nArg, const char * zName,
00559                 void (**pxFunc)(void *, int, rpmvArg *),
00560                 void ** ppArg)
00561 {
00562     int rc = SQLITE_OK;
00563 VTDBG(vt, (stderr, "<-- %s(%p,%d,%s,%p,%p) rc %d\n", __FUNCTION__, vt, nArg, zName, pxFunc, ppArg, rc));
00564     return rc;
00565 }
00566 
00567 int rpmvtRename(rpmvt vt, const char * zNew)
00568 {
00569     int rc = SQLITE_OK;
00570 VTDBG(vt, (stderr, "<-- %s(%p,%s) rc %d\n", __FUNCTION__, vt, zNew, rc));
00571     return rc;
00572 }
00573 #endif /* defined(WITH_SQLITE) */
00574 
00575 /*==============================================================*/
00576 
00577 #define VCDBG(_vc, _l) if ((_vc)->debug) fprintf _l
00578 #define VCDBGNOISY(_vc, _l) if ((_vc)->debug < 0) fprintf _l
00579 
00583 static void rpmvcFini(void * _VC)
00584         /*@globals fileSystem @*/
00585         /*@modifies *_VC, fileSystem @*/
00586 {
00587     struct rpmVC_s * VC = _VC;
00588     rpmvc vc = &VC->vc;
00589 
00590 VCDBGNOISY(vc, (stderr, "==> %s(%p)\n", __FUNCTION__, vc));
00591     if (vc->vt)
00592         (void) rpmvtFree(vc->vt);
00593     vc->vt = NULL;
00594 }
00595 
00596 /*@unchecked@*/ /*@only@*/ /*@null@*/
00597 rpmioPool _rpmvcPool;
00598 
00599 static rpmvc rpmvcGetPool(/*@null@*/ rpmioPool pool)
00600         /*@globals _rpmvcPool, fileSystem @*/
00601         /*@modifies pool, _rpmvcPool, fileSystem @*/
00602 {
00603     struct rpmVC_s * VC;
00604 
00605     if (_rpmvcPool == NULL) {
00606         _rpmvcPool = rpmioNewPool("vc", sizeof(*VC), -1, _rpmvc_debug,
00607                         NULL, NULL, rpmvcFini);
00608         pool = _rpmvcPool;
00609     }
00610     VC = (struct rpmVC_s *) rpmioGetPool(pool, sizeof(*VC));
00611     memset(((char *)VC)+sizeof(VC->_item), 0, sizeof(*VC)-sizeof(VC->_item));
00612     return &VC->vc;
00613 }
00614 
00615 rpmvc rpmvcNew(rpmvt vt, int nrows)
00616 {
00617     rpmvc vc = rpmvcLink(rpmvcGetPool(_rpmvcPool));
00618 
00619     vc->vt = rpmvtLink(vt);
00620     vc->ix = -1;
00621 
00622     vc->debug = _rpmvc_debug;
00623     vc->nrows = nrows;
00624     vc->vd = NULL;
00625 
00626     return vc;
00627 }
00628 
00629 /*==============================================================*/
00630 
00631 #if defined(WITH_SQLITE)
00632 
00633 int rpmvcOpen(rpmvt vt, rpmvc * vcp)
00634 {
00635     rpmvc vc = rpmvcNew(vt, vt->ac);
00636     int rc = SQLITE_OK;
00637 
00638     if (vcp)
00639         *vcp = vc;
00640     else
00641         (void) rpmvcFree(vc);
00642 
00643     return rc;
00644 }
00645 
00646 int rpmvcClose(rpmvc vc)
00647 {
00648     /* XXX unnecessary but the debug spewage is confusing. */
00649     if (vc->vt)
00650         (void) rpmvtFree(vc->vt);
00651     vc->vt = NULL;
00652     (void) rpmvcFree(vc);
00653     return 0;   /* SQLITE_OK */
00654 }
00655 
00656 int rpmvcFilter(rpmvc vc, int idxNum, const char * idxStr,
00657                 int argc, rpmvArg * _argv)
00658 {
00659     sqlite3_value ** argv = (sqlite3_value **) _argv;
00660     int rc = SQLITE_OK;
00661 
00662 VCDBGNOISY(vc, (stderr, "--> %s(%p,%d,%s,%p[%u]) [%d:%d]\n", __FUNCTION__, vc, idxNum, idxStr, argv, (unsigned)argc, vc->ix, vc->nrows));
00663 dumpArgv(__FUNCTION__, argc, _argv);
00664 
00665     if (vc->nrows > 0)
00666         vc->ix = 0;
00667 
00668 VCDBGNOISY(vc, (stderr, "<-- %s(%p,%d,%s,%p[%u]) [%d:%d] rc %d\n", __FUNCTION__, vc, idxNum, idxStr, argv, (unsigned)argc, vc->ix, vc->nrows, rc));
00669 
00670     return rc;
00671 }
00672 
00673 int rpmvcNext(rpmvc vc)
00674 {
00675     int rc = SQLITE_OK;
00676 
00677     if (vc->ix >= 0 && vc->ix < vc->nrows)              /* XXX needed? */
00678         vc->ix++;
00679 
00680 if (!(vc->ix >= 0 && vc->ix < vc->nrows))
00681 VCDBGNOISY(vc, (stderr, "<-- %s(%p) rc %d (%d:%d)\n", __FUNCTION__, vc, rc, vc->ix, vc->nrows));
00682     return rc;
00683 }
00684 
00685 int rpmvcEof(rpmvc vc)
00686 {
00687     int rc = (vc->ix >= 0 && vc->ix < vc->nrows ? 0 : 1);
00688     
00689 if (rc)
00690 VCDBGNOISY(vc, (stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, vc, rc));
00691     return rc;
00692 }
00693 
00694 /*==============================================================*/
00695 
00696 int rpmvcColumn(rpmvc vc, void * _pContext, int colx)
00697 {
00698     sqlite3_context * pContext = (sqlite3_context *) _pContext;
00699     rpmvt vt = vc->vt;
00700     rpmvd vd = vt->vd;
00701     const char * path = vt->av[vc->ix];
00702     const char * col = vt->cols[colx];
00703     int rc = SQLITE_OK;
00704 
00705     size_t nb;
00706     miRE mire = NULL;
00707     int noffsets = 0;
00708     int * offsets = NULL;
00709     int xx;
00710     int i;
00711 
00712     /* Use a PCRE pattern for parsing column value. */
00713     if (vd->regex) {
00714         mire = mireNew(RPMMIRE_REGEX, 0);
00715         xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, 0, NULL);
00716         xx = mireRegcomp(mire, vd->regex);      /* XXX move to rpmvtNew */
00717 
00718         noffsets = 10 * 3;
00719         nb = noffsets * sizeof(*offsets);
00720         offsets = memset(alloca(nb), -1, nb);
00721         xx = mireSetEOptions(mire, offsets, noffsets);
00722 
00723 nb = strlen(path);
00724         xx = mireRegexec(mire, path, nb);
00725 assert(xx == 0);
00726 
00727         for (i = 0; i < noffsets; i += 2) {
00728             if (offsets[i] < 0)
00729                 continue;
00730 assert(offsets[i  ] >= 0 && offsets[i  ] <= (int)nb);
00731 assert(offsets[i+1] >= 0 && offsets[i+1] <= (int)nb);
00732             offsets[i+1] -= offsets[i]; /* XXX convert offset to length */
00733 VCDBGNOISY(vc, (stderr, "\t%d [%d,%d] %.*s\n", i/2, offsets[i], offsets[i+1], offsets[i+1], path+offsets[i]));
00734         }
00735 
00736     }
00737 
00738     if (!strcmp(col, "path"))
00739         sqlite3_result_text(pContext, path, -1, SQLITE_STATIC);
00740     else
00741     if (vd->regex) {
00742         /* Use a PCRE pattern for parsing column value. */
00743 assert(vt->fields);
00744         for (i = 0; i < vt->nfields; i++) {
00745             /* Slurp file contents for unknown field values. */
00746             /* XXX procdb/yumdb */
00747             /* XXX uri's? */
00748             if (path[0] == '/' && !strcmp("*", vt->fields[i])) {
00749                 const char * fn = rpmGetPath(path, "/", col, NULL);
00750                 if (!Access(fn, R_OK)) {
00751                     rpmiob iob = NULL;
00752                     xx = rpmiobSlurp(fn, &iob);
00753                     sqlite3_result_text(pContext, rpmiobStr(iob), rpmiobLen(iob), SQLITE_TRANSIENT);
00754                     iob = rpmiobFree(iob);
00755                 } else
00756                     sqlite3_result_null(pContext);
00757                 break;
00758             } else
00759             if (!strcmp(col, vt->fields[i])) {
00760                 int ix = 2 * (i + 1);
00761                 const char * s = path + offsets[ix];
00762                 size_t ns = offsets[ix+1]; /* XXX convert offset to length */
00763                 sqlite3_result_text(pContext, s, ns, SQLITE_STATIC);
00764                 break;
00765             }
00766         }
00767         if (i == vt->nfields)
00768             sqlite3_result_null(pContext);
00769     } else
00770     if (vd->split && strlen(vd->split) == 1 && vt->nfields > 0) {
00771         /* Simple argv split on a separator char. */
00772         /* XXX using argvSplit has extra malloc's, needs SQLITE_TRANSIENT */
00773         ARGV_t av = NULL;       /* XXX move to rpmvcNext for performance */
00774         xx = argvSplit(&av, path, vd->split);
00775 assert(vt->fields);
00776         for (i = 0; i < vt->nfields; i++) {
00777             if (strcmp(col, vt->fields[i]))
00778                 continue;
00779             sqlite3_result_text(pContext, av[i], -1, SQLITE_TRANSIENT);
00780             break;
00781         }
00782         if (i == vt->nfields)
00783             sqlite3_result_null(pContext);
00784         av = argvFree(av);
00785     } else
00786         sqlite3_result_null(pContext);  /* XXX unnecessary */
00787 
00788     if (mire) {
00789         xx = mireSetEOptions(mire, NULL, 0);
00790         mire = mireFree(mire);
00791     }
00792 
00793 if (rc)
00794 VCDBG(vc, (stderr, "<-- %s(%p,%p,%d) rc %d\n", __FUNCTION__, vc, pContext, colx, rc));
00795 
00796     return rc;
00797 }
00798 
00799 int rpmvcRowid(rpmvc vc, int64_t * pRowid)
00800 {
00801     int rc = SQLITE_OK;
00802 
00803     if (pRowid)
00804         *pRowid = vc->ix;
00805 
00806 if (rc)
00807 VCDBG(vc, (stderr, "<-- %s(%p,%p) rc %d rowid 0x%llx\n", __FUNCTION__, vc, pRowid, rc, (unsigned long long)(pRowid ? *pRowid : 0xf00)));
00808     return rc;
00809 }
00810 #endif /* defined(WITH_SQLITE) */
00811 
00812 /*==============================================================*/
00813 
00814 static void _rpmsqlDebugDump(rpmsql sql,
00815                 const char * _func, const char * _fn, unsigned _ln)
00816 {
00817 SQLDBG((stderr, "==> %s:%u %s(%p) _rpmsqlI %p\n", _fn, _ln, _func, sql, _rpmsqlI));
00818     if (sql) {
00819         fprintf(stderr, "\t    flags: 0x%x\n", sql->flags);
00820         fprintf(stderr, "\t       av: %p[%u]\n", sql->av, (unsigned)argvCount(sql->av));
00821         fprintf(stderr, "\t        I: %p\n", sql->I);
00822         fprintf(stderr, "\t        S: %p\n", sql->S);
00823         fprintf(stderr, "\t     init: %s\n", sql->zInitFile);
00824         fprintf(stderr, "\t database: %s\n", sql->zDbFilename);
00825         fprintf(stderr, "\t    table: %s\n", sql->zDestTable);
00826 
00827         fprintf(stderr, "\t     mode: 0x%x\n", sql->mode);
00828         fprintf(stderr, "\t      cnt: 0x%x\n", sql->cnt);
00829         fprintf(stderr, "\t      iob: %p\n", sql->iob);
00830         fprintf(stderr, "\t   IN ifd: %p\n", sql->ifd);
00831         fprintf(stderr, "\t  OUT ofd: %p\n", sql->ofd);
00832         fprintf(stderr, "\t  LOG lfd: %p\n", sql->lfd);
00833         fprintf(stderr, "\tTRACE tfd: %p\n", sql->tfd);
00834 
00835         if (sql->explainPrev.valid) {
00836             fprintf(stderr, "\t  explain:\n");
00837             fprintf(stderr, "\t\t mode: 0x%x\n", sql->explainPrev.mode);
00838             fprintf(stderr, "\t\tflags: 0x%x\n", sql->explainPrev.flags);
00839         }
00840 
00841         fprintf(stderr, "\tseparator: %.*s\n", (int)sizeof(sql->separator), sql->separator);
00842         fprintf(stderr, "\tnullvalue: %.*s\n", (int)sizeof(sql->nullvalue), sql->nullvalue);
00843         fprintf(stderr, "\t  outfile: %s\n", sql->outfile);
00844         fprintf(stderr, "\t     home: %s\n", sql->zHome);
00845         fprintf(stderr, "\t   initrc: %s\n", sql->zInitrc);
00846         fprintf(stderr, "\t  history: %s\n", sql->zHistory);
00847         fprintf(stderr, "\t   prompt: %s\n", sql->zPrompt);
00848         fprintf(stderr, "\t continue: %s\n", sql->zContinue);
00849 
00850         fprintf(stderr, "\t      buf: %p[%u]\n", sql->buf, (unsigned)sql->nbuf);
00851         fprintf(stderr, "\t        b: %p[%u]\n", sql->b, (unsigned)sql->nb);
00852     }
00853 }
00854 #define rpmsqlDebugDump(_sql) \
00855         _rpmsqlDebugDump(_sql, __FUNCTION__, __FILE__, __LINE__)
00856 
00857 #if defined(WITH_SQLITE)
00858 
00863 /*@mayexit@*/ /*@printflike@*/
00864 static void rpmsql_error(int lvl, const char *fmt, ...)
00865 #if defined(__GNUC__) && __GNUC__ >= 2
00866         __attribute__((format (printf, 2, 3)))
00867 #endif
00868         /*@globals fileSystem @*/
00869         /*@modifies fileSystem @*/;
00870 static void
00871 rpmsql_error(int lvl, const char *fmt, ...)
00872 {
00873     va_list ap;
00874 
00875     (void) fflush(NULL);
00876     if (lvl >= 1)
00877         (void) fprintf(stderr, "Error: ");
00878     va_start(ap, fmt);
00879     (void) vfprintf(stderr, fmt, ap);
00880     va_end (ap);
00881     (void) fprintf(stderr, "\n");
00882     if (lvl > 1)
00883         exit(EXIT_FAILURE);
00884 }
00885 
00891 int rpmsqlCmd(/*@null@*/ rpmsql sql, const char * msg, void * _db, int rc)
00892         /*@globals fileSystem @*/
00893         /*@modifies fileSystem @*/
00894 {
00895     sqlite3 * db;
00896 
00897     switch (rc) {
00898     case SQLITE_OK:
00899     case SQLITE_ROW:
00900     case SQLITE_DONE:
00901         /* XXX ignore noisy debug spewage */
00902         if (1 || !_rpmsql_debug)
00903             break;
00904         /*@fallthrough@*/
00905     default:
00906         /* XXX system sqlite3 w loadable modules */
00907         if (sql)
00908             db = (sqlite3 *) (_db ? _db : sql->I);
00909         else
00910             db = (sqlite3 *) _db;
00911         rpmsql_error(0, "sqlite3_%s(%p): rc(%d) %s", msg, db, rc,
00912                 sqlite3_errmsg(db));
00913         break;
00914     }
00915     return rc;
00916 }
00917 #endif  /* defined(WITH_SQLITE) */
00918 
00919 /*==============================================================*/
00920 
00921 #if defined(WITH_SQLITE)
00922 
00926 static void _rpmsqlBeginTimer(rpmsql sql)
00927 {
00928     if (sql->enableTimer)
00929         getrusage(RUSAGE_SELF, &sql->sBegin);
00930 }
00931 
00932 /* Return the difference of two time_structs in seconds */
00933 static double timeDiff(struct timeval *pStart, struct timeval *pEnd)
00934 {
00935     return (pEnd->tv_usec - pStart->tv_usec) * 0.000001 +
00936         (double) (pEnd->tv_sec - pStart->tv_sec);
00937 }
00938 
00943 static void _rpmsqlEndTimer(rpmsql sql)
00944 {
00945     if (sql->enableTimer) {
00946         struct rusage sEnd;
00947         char b[BUFSIZ];
00948         size_t nb;
00949         size_t nw;
00950 assert(sql->ofd);
00951         getrusage(RUSAGE_SELF, &sEnd);
00952         snprintf(b, sizeof(b), "CPU Time: user %f sys %f\n",
00953                timeDiff(&sql->sBegin.ru_utime, &sEnd.ru_utime),
00954                timeDiff(&sql->sBegin.ru_stime, &sEnd.ru_stime));
00955         nb = strlen(b);
00956         nw = Fwrite(b, 1, nb, sql->ofd);
00957 assert(nb == nw);
00958     }
00959 }
00960 
00961 #define BEGIN_TIMER(_sql)        _rpmsqlBeginTimer(_sql)
00962 #define END_TIMER(_sql)          _rpmsqlEndTimer(_sql)
00963 #define HAS_TIMER 1
00964 
00965 #define ArraySize(X)  (int)(sizeof(X)/sizeof(X[0]))
00966 #endif  /* defined(WITH_SQLITE) */
00967 
00968 /*==============================================================*/
00969 
00974 static rpmsql rpmsqlI(void)
00975         /*@globals _rpmsqlI @*/
00976         /*@modifies _rpmsqlI @*/
00977 {
00978     if (_rpmsqlI == NULL)
00979         _rpmsqlI = rpmsqlNew(NULL, 0);
00980 SQLDBG((stderr, "<== %s() _rpmsqlI %p\n", __FUNCTION__, _rpmsqlI));
00981     return _rpmsqlI;
00982 }
00983 
00984 #if defined(WITH_SQLITE)
00985 
00989 /*@printflike@*/
00990 static int rpmsqlFprintf(rpmsql sql, const char *fmt, ...)
00991 #if defined(__GNUC__) && __GNUC__ >= 2
00992         __attribute__((format (printf, 2, 3)))
00993 #endif
00994         /*@*/;
00995 static int rpmsqlFprintf(rpmsql sql, const char *fmt, ...)
00996 {
00997     char b[BUFSIZ];
00998     size_t nb = sizeof(b);
00999     int rc;
01000     va_list ap;
01001 
01002     if (sql == NULL) sql = rpmsqlI();
01003 assert(sql);
01004 
01005     /* Format the output */
01006     va_start(ap, fmt);
01007     rc = vsnprintf(b, nb, fmt, ap);
01008     va_end(ap);
01009     /* XXX just in case */
01010     if (!(rc >= 0 && rc < (int)nb))
01011         rc = nb - 1;
01012     b[rc] = '\0';
01013 
01014     /* Dispose of the output. */
01015     if (sql->ofd) {
01016         size_t nw = Fwrite(b, 1, rc, sql->ofd);
01017 assert((int)nw == rc);
01018     }
01019     if (sql->iob)
01020         (void) rpmiobAppend(sql->iob, b, 0);
01021 
01022     return rc;
01023 }
01024 
01031 #ifdef SQLITE_ENABLE_IOTRACE
01032 static void iotracePrintf(const char *zFormat, ...)
01033 {
01034     char * z;
01035     size_t nz;
01036     size_t nw;
01037     va_list ap;
01038 
01039     if (_rpmsqlI == NULL || _rpmsqlI->tfd == NULL)
01040         return;
01041     va_start(ap, zFormat);
01042     z = sqlite3_vmprintf(zFormat, ap);
01043     va_end(ap);
01044     nz = strlen(z);
01045     nw = Fwrite(z, 1, nz, sql->tfd);
01046 assert(nz == nw);
01047     sqlite3_free(z);
01048 }
01049 #endif
01050 
01051 #if defined(SQLITE_CONFIG_LOG)
01052 
01056 static void shellLog(void *_sql, int iErrCode, const char *zMsg)
01057 {
01058     rpmsql sql = (rpmsql) _sql;
01059     if (sql && sql->lfd) {
01060         char num[32];
01061         int xx = snprintf(num, sizeof(num), "(%d) ", iErrCode);
01062         const char * t = rpmExpand(num, zMsg, "\n", NULL);
01063         size_t nt = strlen(t);
01064         size_t nw = Fwrite(t, 1, nt, sql->lfd);
01065 assert(nt == nw);
01066         xx = Fflush(sql->lfd);
01067     }
01068 }
01069 #endif
01070 
01071 /*==============================================================*/
01077 #ifdef  NOTYET  /* XXX figger multibyte char's. */
01078 #define sqliteNextChar(X)       while( (0xc0&*++(X))==0x80 ){}
01079 #define sqliteCharVal(X)        sqlite3ReadUtf8(X)
01080 #else
01081 #define sqliteNextChar(X)       while( (     *++(X))       ) break
01082 #define sqliteCharVal(X)                (int)(*(X))
01083 #endif
01084 
01085 #include        <math.h>
01086 
01101 #define GEN_MATH_WRAP_DOUBLE_1(name, function, domain) \
01102 static void name(sqlite3_context *context, int argc, sqlite3_value **argv) {\
01103   double rVal = 0.0;\
01104 assert(argc==1);\
01105   switch (sqlite3_value_type(argv[0])) {\
01106     case SQLITE_NULL:\
01107       sqlite3_result_null(context);\
01108       break;\
01109     default:\
01110       rVal = sqlite3_value_double(argv[0]);\
01111       if (domain)\
01112         sqlite3_result_error(context, "domain error", -1);\
01113       else\
01114         sqlite3_result_double(context, function(rVal));\
01115       break;\
01116   }\
01117 }
01118 
01124 GEN_MATH_WRAP_DOUBLE_1(sqrtFunc, sqrt, rVal < 0)
01125 
01126 /* trignometric functions */
01127 GEN_MATH_WRAP_DOUBLE_1(acosFunc, acos, rVal < -1.0 || rVal > 1.0)
01128 GEN_MATH_WRAP_DOUBLE_1(asinFunc, asin, rVal < -1.0 || rVal > 1.0)
01129 GEN_MATH_WRAP_DOUBLE_1(atanFunc, atan, 0)
01130 
01136 #ifdef REFERENCE
01137 static double acosh(double x)
01138 {
01139     return log(x + sqrt(x * x - 1.0));
01140 }
01141 #endif
01142 
01143 GEN_MATH_WRAP_DOUBLE_1(acoshFunc, acosh, rVal < 1)
01144 #ifdef REFERENCE
01145 static double asinh(double x)
01146 {
01147     return log(x + sqrt(x * x + 1.0));
01148 }
01149 #endif
01150 
01151 GEN_MATH_WRAP_DOUBLE_1(asinhFunc, asinh, 0)
01152 #ifdef REFERENCE
01153 static double atanh(double x)
01154 {
01155     return (1.0 / 2.0) * log((1 + x) / (1 - x));
01156 }
01157 #endif
01158 
01159 GEN_MATH_WRAP_DOUBLE_1(atanhFunc, atanh, rVal > 1.0 || rVal < -1.0)
01160 
01161 
01164 static double cot(double x)
01165 {
01166     return 1.0 / tan(x);
01167 }
01168 
01169 GEN_MATH_WRAP_DOUBLE_1(sinFunc, sin, 0)
01170 GEN_MATH_WRAP_DOUBLE_1(cosFunc, cos, 0)
01171 GEN_MATH_WRAP_DOUBLE_1(tanFunc, tan, 0)         /* XXX DOMAIN */
01172 GEN_MATH_WRAP_DOUBLE_1(cotFunc, cot, 0)         /* XXX DOMAIN */
01173 
01174 static double coth(double x)
01175 {
01176     return 1.0 / tanh(x);
01177 }
01178 
01183 #ifdef REFERENCE
01184 static double sinh(double x)
01185 {
01186     return (exp(x) - exp(-x)) / 2.0;
01187 }
01188 #endif
01189 GEN_MATH_WRAP_DOUBLE_1(sinhFunc, sinh, 0)
01190 
01191 #ifdef REFERENCE
01192 static double cosh(double x)
01193 {
01194     return (exp(x) + exp(-x)) / 2.0;
01195 }
01196 #endif
01197 GEN_MATH_WRAP_DOUBLE_1(coshFunc, cosh, 0)
01198 
01199 #ifdef REFERENCE
01200 static double tanh(double x)
01201 {
01202     return sinh(x) / cosh(x);
01203 }
01204 #endif
01205 GEN_MATH_WRAP_DOUBLE_1(tanhFunc, tanh, 0)
01206 GEN_MATH_WRAP_DOUBLE_1(cothFunc, coth, 0)       /* XXX DOMAIN */
01207 
01211 #ifdef REFERENCE
01212 static double log10(double x)
01213 {
01214     static double l10 = -1.0;
01215     if (l10 < 0.0) {
01216         l10 = log(10.0);
01217     }
01218     return log(x) / l10;
01219 }
01220 #endif
01221 GEN_MATH_WRAP_DOUBLE_1(logFunc, log, rVal <= 0.0)
01222 GEN_MATH_WRAP_DOUBLE_1(log10Func, log10, rVal <= 0.0)
01223 GEN_MATH_WRAP_DOUBLE_1(expFunc, exp, 0)
01224 
01228 #ifndef M_PI
01229 
01233 #define M_PI 3.14159265358979323846
01234 #endif
01235 
01239 static double deg2rad(double x)
01240 {
01241     return x * M_PI / 180.0;
01242 }
01243 
01247 static double rad2deg(double x)
01248 {
01249     return 180.0 * x / M_PI;
01250 }
01251 GEN_MATH_WRAP_DOUBLE_1(rad2degFunc, rad2deg, 0)
01252 GEN_MATH_WRAP_DOUBLE_1(deg2radFunc, deg2rad, 0)
01253 
01257 static void piFunc(sqlite3_context * context,
01258                 int argc, sqlite3_value ** argv)
01259 {
01260     sqlite3_result_double(context, M_PI);
01261 }
01262 
01268 static void squareFunc(sqlite3_context * context,
01269                 int argc, sqlite3_value ** argv)
01270 {
01271     double rVal = 0.0;
01272     int64_t iVal;
01273 
01274 assert(argc == 2);
01275     switch (sqlite3_value_type(argv[0])) {
01276     case SQLITE_INTEGER:
01277         iVal = sqlite3_value_int64(argv[0]);
01278         sqlite3_result_int64(context, iVal * iVal);
01279         break;
01280     case SQLITE_NULL:
01281         sqlite3_result_null(context);
01282         break;
01283     default:
01284         rVal = sqlite3_value_double(argv[0]);
01285         sqlite3_result_double(context, rVal * rVal);
01286         break;
01287     }
01288 }
01289 
01295 static void powerFunc(sqlite3_context * context,
01296                 int argc, sqlite3_value ** argv)
01297 {
01298     double r1 = 0.0;
01299     double r2 = 0.0;
01300 
01301 assert(argc == 2);
01302 
01303     if (sqlite3_value_type(argv[0]) == SQLITE_NULL
01304         || sqlite3_value_type(argv[1]) == SQLITE_NULL) {
01305         sqlite3_result_null(context);
01306     } else {
01307         r1 = sqlite3_value_double(argv[0]);
01308         r2 = sqlite3_value_double(argv[1]);
01309         if (r1 <= 0.0) {
01310             /* base must be positive */
01311             sqlite3_result_error(context, "domain error", -1);
01312         } else {
01313             sqlite3_result_double(context, pow(r1, r2));
01314         }
01315     }
01316 }
01317 
01321 static void atn2Func(sqlite3_context * context,
01322                 int argc, sqlite3_value ** argv)
01323 {
01324     double r1 = 0.0;
01325     double r2 = 0.0;
01326 
01327 assert(argc == 2);
01328 
01329     if (sqlite3_value_type(argv[0]) == SQLITE_NULL
01330         || sqlite3_value_type(argv[1]) == SQLITE_NULL) {
01331         sqlite3_result_null(context);
01332     } else {
01333         r1 = sqlite3_value_double(argv[0]);
01334         r2 = sqlite3_value_double(argv[1]);
01335         sqlite3_result_double(context, atan2(r1, r2));
01336     }
01337 }
01338 
01345 static void signFunc(sqlite3_context * context,
01346                 int argc, sqlite3_value ** argv)
01347 {
01348     double rVal = 0.0;
01349     int64_t iVal;
01350 
01351 assert(argc == 1);
01352     switch (sqlite3_value_type(argv[0])) {
01353     case SQLITE_INTEGER:
01354         iVal = sqlite3_value_int64(argv[0]);
01355         iVal = (iVal > 0) ? 1 : (iVal < 0) ? -1 : 0;
01356         sqlite3_result_int64(context, iVal);
01357         break;
01358     case SQLITE_NULL:
01359         sqlite3_result_null(context);
01360         break;
01361     default:
01362         /* 2nd change below. Line for abs was: if( rVal<0 ) rVal = rVal * -1.0;  */
01363 
01364         rVal = sqlite3_value_double(argv[0]);
01365         rVal = (rVal > 0) ? 1 : (rVal < 0) ? -1 : 0;
01366         sqlite3_result_double(context, rVal);
01367         break;
01368     }
01369 }
01370 
01374 static void ceilFunc(sqlite3_context * context,
01375                 int argc, sqlite3_value ** argv)
01376 {
01377     double rVal = 0.0;
01378     int64_t iVal;
01379 
01380 assert(argc == 1);
01381     switch (sqlite3_value_type(argv[0])) {
01382     case SQLITE_INTEGER:
01383         iVal = sqlite3_value_int64(argv[0]);
01384         sqlite3_result_int64(context, iVal);
01385         break;
01386     case SQLITE_NULL:
01387         sqlite3_result_null(context);
01388         break;
01389     default:
01390         rVal = sqlite3_value_double(argv[0]);
01391         sqlite3_result_int64(context, ceil(rVal));
01392         break;
01393     }
01394 }
01395 
01399 static void floorFunc(sqlite3_context * context,
01400                 int argc, sqlite3_value ** argv)
01401 {
01402     double rVal = 0.0;
01403     int64_t iVal;
01404 
01405 assert(argc == 1);
01406     switch (sqlite3_value_type(argv[0])) {
01407     case SQLITE_INTEGER:
01408         iVal = sqlite3_value_int64(argv[0]);
01409         sqlite3_result_int64(context, iVal);
01410         break;
01411     case SQLITE_NULL:
01412         sqlite3_result_null(context);
01413         break;
01414     default:
01415         rVal = sqlite3_value_double(argv[0]);
01416         sqlite3_result_int64(context, floor(rVal));
01417         break;
01418     }
01419 }
01420 
01425 static void replicateFunc(sqlite3_context * context,
01426                 int argc, sqlite3_value ** argv)
01427 {
01428     unsigned char *z;           /* input string */
01429     unsigned char *zo;          /* result string */
01430     int iCount;                 /* times to repeat */
01431     size_t nLen;                /* length of the input string (no multibyte considerations) */
01432     size_t nTLen;               /* length of the result string (no multibyte considerations) */
01433     int i = 0;
01434 
01435     if (argc != 2 || SQLITE_NULL == sqlite3_value_type(argv[0]))
01436         return;
01437 
01438     iCount = sqlite3_value_int64(argv[1]);
01439 
01440     if (iCount < 0) {
01441         sqlite3_result_error(context, "domain error", -1);
01442     } else {
01443         nLen = sqlite3_value_bytes(argv[0]);
01444         nTLen = nLen * iCount;
01445         z = xmalloc(nTLen + 1);
01446         zo = xmalloc(nLen + 1);
01447         strcpy((char *) zo, (char *) sqlite3_value_text(argv[0]));
01448 
01449         for (i = 0; i < iCount; ++i)
01450             strcpy((char *) (z + i * nLen), (char *) zo);
01451 
01452         sqlite3_result_text(context, (char *) z, -1, free);
01453         zo = _free(zo);
01454     }
01455 }
01456 
01457 static void properFunc(sqlite3_context * context,
01458                 int argc, sqlite3_value ** argv)
01459 {
01460     const unsigned char *z;     /* input string */
01461     unsigned char *zo;          /* output string */
01462     unsigned char *zt;          /* iterator */
01463     char r;
01464     int c = 1;
01465 
01466 assert(argc == 1);
01467     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
01468         sqlite3_result_null(context);
01469         return;
01470     }
01471 
01472     z = sqlite3_value_text(argv[0]);
01473     zo = (unsigned char *) xstrdup((const char *)z);
01474     zt = zo;
01475 
01476     while ((r = *(z++)) != 0) {
01477         if (xisblank(r)) {
01478             c = 1;
01479         } else {
01480             r = (c == 1) ? xtoupper(r) : xtolower(r);
01481             c = 0;
01482         }
01483         *(zt++) = r;
01484     }
01485     *zt = '\0';
01486 
01487     sqlite3_result_text(context, (char *) zo, -1, free);
01488 }
01489 
01490 #ifdef  NOTYET  /* XXX figger multibyte char's. */
01491 
01497 static void padlFunc(sqlite3_context * context,
01498                 int argc, sqlite3_value ** argv)
01499 {
01500     size_t ilen;                /* length to pad to */
01501     size_t zl;                  /* length of the input string (UTF-8 chars) */
01502     size_t i;
01503     const char *zi;             /* input string */
01504     char *zo;                   /* output string */
01505     char *zt;
01506 
01507 assert(argc == 2);
01508     if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
01509         sqlite3_result_null(context);
01510     } else {
01511         zi = (const char *) sqlite3_value_text(argv[0]);
01512         ilen = sqlite3_value_int64(argv[1]);
01513         /* check domain */
01514         if (ilen < 0) {
01515             sqlite3_result_error(context, "domain error", -1);
01516             return;
01517         }
01518         zl = sqlite3utf8CharLen(zi, -1);
01519         if (zl >= ilen) {
01520             /* string is longer than the requested pad length, return the same string (dup it) */
01521             sqlite3_result_text(context, xstrdup(zi), -1, free);
01522         } else {
01523             zo = xmalloc(strlen(zi) + ilen - zl + 1);
01524             zt = zo;
01525             for (i = 1; i + zl <= ilen; ++i)
01526                 *(zt++) = ' ';
01527             /* no need to take UTF-8 into consideration here */
01528             strcpy(zt, zi);
01529             sqlite3_result_text(context, zo, -1, free);
01530         }
01531     }
01532 }
01533 
01540 static void padrFunc(sqlite3_context * context,
01541                 int argc, sqlite3_value ** argv)
01542 {
01543     size_t ilen;                /* length to pad to */
01544     size_t zl;                  /* length of the input string (UTF-8 chars) */
01545     size_t zll;                 /* length of the input string (bytes) */
01546     size_t i;
01547     const char *zi;             /* input string */
01548     char *zo;                   /* output string */
01549     char *zt;
01550 
01551 assert(argc == 2);
01552     if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
01553         sqlite3_result_null(context);
01554     } else {
01555         int64_t _ilen;
01556         zi = (const char *) sqlite3_value_text(argv[0]);
01557         _ilen = sqlite3_value_int64(argv[1]);
01558         /* check domain */
01559         if (_ilen < 0) {
01560             sqlite3_result_error(context, "domain error", -1);
01561             return;
01562         }
01563         ilen = _ilen;
01564         zl = sqlite3utf8CharLen(zi, -1);
01565         if (zl >= ilen) {
01566             /* string is longer than the requested pad length, return the same string (dup it) */
01567             sqlite3_result_text(context, xstrdup(zi), -1, free);
01568         } else {
01569             zll = strlen(zi);
01570             zo = xmalloc(zll + ilen - zl + 1);
01571             zt = strcpy(zo, zi) + zll;
01572             for (i = 1; i + zl <= ilen; ++i)
01573                 *(zt++) = ' ';
01574             *zt = '\0';
01575             sqlite3_result_text(context, zo, -1, free);
01576         }
01577     }
01578 }
01579 
01587 static void padcFunc(sqlite3_context * context,
01588                 int argc, sqlite3_value ** argv)
01589 {
01590     size_t ilen;                /* length to pad to */
01591     size_t zl;                  /* length of the input string (UTF-8 chars) */
01592     size_t zll;                 /* length of the input string (bytes) */
01593     size_t i;
01594     const char *zi;             /* input string */
01595     char *zo;                   /* output string */
01596     char *zt;
01597 
01598 assert(argc == 2);
01599     if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
01600         sqlite3_result_null(context);
01601     } else {
01602         int64_t _ilen;
01603         zi = (const char *) sqlite3_value_text(argv[0]);
01604         _ilen = sqlite3_value_int64(argv[1]);
01605         /* check domain */
01606         if (_ilen < 0) {
01607             sqlite3_result_error(context, "domain error", -1);
01608             return;
01609         }
01610         ilen = _ilen;
01611         zl = sqlite3utf8CharLen(zi, -1);
01612         if (zl >= ilen) {
01613             /* string is longer than the requested pad length, return the same string (dup it) */
01614             sqlite3_result_text(context, xstrdup(zi), -1, free);
01615         } else {
01616             zll = strlen(zi);
01617             zo = xmalloc(zll + ilen - zl + 1);
01618             zt = zo;
01619             for (i = 1; 2 * i + zl <= ilen; ++i)
01620                 *(zt++) = ' ';
01621             strcpy(zt, zi);
01622             zt += zll;
01623             for (; i + zl <= ilen; ++i)
01624                 *(zt++) = ' ';
01625             *zt = '\0';
01626             sqlite3_result_text(context, zo, -1, free);
01627         }
01628     }
01629 }
01630 #endif
01631 
01636 static void strfilterFunc(sqlite3_context * context,
01637                 int argc, sqlite3_value ** argv)
01638 {
01639     const char *zi1;            /* first parameter string (searched string) */
01640     const char *zi2;            /* second parameter string (vcontains valid characters) */
01641     const char *z1;
01642     const char *z21;
01643     const char *z22;
01644     char *zo;                   /* output string */
01645     char *zot;
01646     int c1;
01647     int c2;
01648 
01649 assert(argc == 2);
01650 
01651     if (sqlite3_value_type(argv[0]) == SQLITE_NULL
01652         || sqlite3_value_type(argv[1]) == SQLITE_NULL) {
01653         sqlite3_result_null(context);
01654     } else {
01655         zi1 = (const char *) sqlite3_value_text(argv[0]);
01656         zi2 = (const char *) sqlite3_value_text(argv[1]);
01657         zo = xmalloc(strlen(zi1) + 1);
01658         zot = zo;
01659         z1 = zi1;
01660         while ((c1 = sqliteCharVal(z1)) != 0) {
01661             z21 = zi2;
01662             while ((c2 = sqliteCharVal(z21)) != 0 && c2 != c1)
01663                 sqliteNextChar(z21);
01664             if (c2 != 0) {
01665                 z22 = z21;
01666                 sqliteNextChar(z22);
01667                 strncpy(zot, z21, z22 - z21);
01668                 zot += z22 - z21;
01669             }
01670             sqliteNextChar(z1);
01671         }
01672         *zot = '\0';
01673 
01674         sqlite3_result_text(context, zo, -1, free);
01675     }
01676 }
01677 
01685 static int _substr(const char *z1, const char *z2, int s, const char **p)
01686 {
01687     int c = 0;
01688     int rVal = -1;
01689     const char *zt1;
01690     const char *zt2;
01691     int c1;
01692     int c2;
01693 
01694     if (*z1 == '\0')
01695         return -1;
01696 
01697     while ((sqliteCharVal(z2) != 0) && (c++) < s)
01698         sqliteNextChar(z2);
01699 
01700     c = 0;
01701     while ((sqliteCharVal(z2)) != 0) {
01702         zt1 = z1;
01703         zt2 = z2;
01704 
01705         do {
01706             c1 = sqliteCharVal(zt1);
01707             c2 = sqliteCharVal(zt2);
01708             sqliteNextChar(zt1);
01709             sqliteNextChar(zt2);
01710         } while (c1 == c2 && c1 != 0 && c2 != 0);
01711 
01712         if (c1 == 0) {
01713             rVal = c;
01714             break;
01715         }
01716 
01717         sqliteNextChar(z2);
01718         ++c;
01719     }
01720     if (p)
01721         *p = z2;
01722     return rVal >= 0 ? rVal + s : rVal;
01723 }
01724 
01732 static void charindexFunc(sqlite3_context * context,
01733                 int argc, sqlite3_value ** argv)
01734 {
01735     const char *z1;     /* s1 string */
01736     const char *z2;     /* s2 string */
01737     int s = 0;
01738     int rVal = 0;
01739 
01740 assert(argc == 2 || argc == 3);
01741     if (SQLITE_NULL == sqlite3_value_type(argv[0])
01742      || SQLITE_NULL == sqlite3_value_type(argv[1])) {
01743         sqlite3_result_null(context);
01744         return;
01745     }
01746 
01747     z1 = (const char *) sqlite3_value_text(argv[0]);
01748     z2 = (const char *) sqlite3_value_text(argv[1]);
01749     if (argc == 3) {
01750         s = sqlite3_value_int(argv[2]) - 1;
01751         if (s < 0)
01752             s = 0;
01753     } else {
01754         s = 0;
01755     }
01756 
01757     rVal = _substr(z1, z2, s, NULL);
01758     sqlite3_result_int(context, rVal + 1);
01759 }
01760 
01765 static void leftFunc(sqlite3_context * context,
01766                 int argc, sqlite3_value ** argv)
01767 {
01768     int c = 0;
01769     int cc = 0;
01770     int l = 0;
01771     const unsigned char *z;     /* input string */
01772     const unsigned char *zt;
01773     unsigned char *rz;          /* output string */
01774 
01775 assert(argc == 2);
01776     if (SQLITE_NULL == sqlite3_value_type(argv[0])
01777      || SQLITE_NULL == sqlite3_value_type(argv[1])) {
01778         sqlite3_result_null(context);
01779         return;
01780     }
01781 
01782     z = sqlite3_value_text(argv[0]);
01783     l = sqlite3_value_int(argv[1]);
01784     zt = z;
01785 
01786     while (sqliteCharVal(zt) && c++ < l)
01787         sqliteNextChar(zt);
01788 
01789     cc = zt - z;
01790 
01791     rz = xmalloc(zt - z + 1);
01792     strncpy((char *) rz, (char *) z, zt - z);
01793     *(rz + cc) = '\0';
01794     sqlite3_result_text(context, (char *) rz, -1, free);
01795 }
01796 
01801 static void rightFunc(sqlite3_context * context,
01802                 int argc, sqlite3_value ** argv)
01803 {
01804     int l = 0;
01805     int c = 0;
01806     int cc = 0;
01807     const char *z;
01808     const char *zt;
01809     const char *ze;
01810     char *rz;
01811 
01812 assert(argc == 2);
01813     if (SQLITE_NULL == sqlite3_value_type(argv[0])
01814      || SQLITE_NULL == sqlite3_value_type(argv[1])) {
01815         sqlite3_result_null(context);
01816         return;
01817     }
01818 
01819     z = (const char *) sqlite3_value_text(argv[0]);
01820     l = sqlite3_value_int(argv[1]);
01821     zt = z;
01822 
01823     while (sqliteCharVal(zt) != 0) {
01824         sqliteNextChar(zt);
01825         ++c;
01826     }
01827 
01828     ze = zt;
01829     zt = z;
01830 
01831     cc = c - l;
01832     if (cc < 0)
01833         cc = 0;
01834 
01835     while (cc-- > 0) {
01836         sqliteNextChar(zt);
01837     }
01838 
01839     rz = xmalloc(ze - zt + 1);
01840     strcpy((char *) rz, (char *) (zt));
01841     sqlite3_result_text(context, (char *) rz, -1, free);
01842 }
01843 
01847 static const char * ltrim(const char *s)
01848 {
01849     while (*s == ' ')
01850         ++s;
01851     return s;
01852 }
01853 
01858 static const char * rtrim(char *s)
01859 {
01860     char *ss = s + strlen(s) - 1;
01861     while (ss >= s && *ss == ' ')
01862         --ss;
01863     *(ss + 1) = '\0';
01864     return s;
01865 }
01866 
01870 static void ltrimFunc(sqlite3_context * context,
01871                 int argc, sqlite3_value ** argv)
01872 {
01873     const char *z;
01874 
01875 assert(argc == 1);
01876     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
01877         sqlite3_result_null(context);
01878         return;
01879     }
01880     z = (const char *) sqlite3_value_text(argv[0]);
01881     sqlite3_result_text(context, xstrdup(ltrim(z)), -1, free);
01882 }
01883 
01887 static void rtrimFunc(sqlite3_context * context,
01888                 int argc, sqlite3_value ** argv)
01889 {
01890     const char *z;
01891 
01892 assert(argc == 1);
01893     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
01894         sqlite3_result_null(context);
01895         return;
01896     }
01897     z = (const char *) sqlite3_value_text(argv[0]);
01898     sqlite3_result_text(context, rtrim(xstrdup(z)), -1, free);
01899 }
01900 
01904 static void trimFunc(sqlite3_context * context,
01905                 int argc, sqlite3_value ** argv)
01906 {
01907     const char *z;
01908 
01909 assert(argc == 1);
01910     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
01911         sqlite3_result_null(context);
01912         return;
01913     }
01914     z = (const char *) sqlite3_value_text(argv[0]);
01915     sqlite3_result_text(context, rtrim(xstrdup(ltrim(z))), -1, free);
01916 }
01917 
01924 static void _append(char **s1, int l1, const char *s2, int l2)
01925 {
01926     *s1 = xrealloc(*s1, (l1 + l2 + 1) * sizeof(char));
01927     strncpy((*s1) + l1, s2, l2);
01928     *(*(s1) + l1 + l2) = '\0';
01929 }
01930 
01934 static void replaceFunc(sqlite3_context * context,
01935                 int argc, sqlite3_value ** argv)
01936 {
01937     const char *z1;             /* string s (first parameter) */
01938     const char *z2;             /* string s1 (second parameter) string to look for */
01939     const char *z3;             /* string s2 (third parameter) string to replace occurrences of s1 with */
01940     size_t lz1;
01941     size_t lz2;
01942     size_t lz3;
01943     int lzo = 0;
01944     char *zo = 0;
01945     int ret = 0;
01946     const char *zt1;
01947     const char *zt2;
01948 
01949 assert(argc == 3);
01950     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
01951         sqlite3_result_null(context);
01952         return;
01953     }
01954 
01955     z1 = (const char *)sqlite3_value_text(argv[0]);
01956     z2 = (const char *)sqlite3_value_text(argv[1]);
01957     z3 = (const char *)sqlite3_value_text(argv[2]);
01958     /* handle possible null values */
01959     if (z2 == NULL)
01960         z2 = "";
01961     if (z3 == NULL)
01962         z3 = "";
01963 
01964     lz1 = strlen(z1);
01965     lz2 = strlen(z2);
01966     lz3 = strlen(z3);
01967 
01968 #if 0
01969     /* special case when z2 is empty (or null) nothing will be changed */
01970     if (0 == lz2) {
01971         sqlite3_result_text(context, xstrdup(z1), -1, free);
01972         return;
01973     }
01974 #endif
01975 
01976     zt1 = z1;
01977     zt2 = z1;
01978 
01979     while (1) {
01980         ret = _substr(z2, zt1, 0, &zt2);
01981 
01982         if (ret < 0)
01983             break;
01984 
01985         _append(&zo, lzo, zt1, zt2 - zt1);
01986         lzo += zt2 - zt1;
01987         _append(&zo, lzo, z3, lz3);
01988         lzo += lz3;
01989 
01990         zt1 = zt2 + lz2;
01991     }
01992     _append(&zo, lzo, zt1, lz1 - (zt1 - z1));
01993     sqlite3_result_text(context, zo, -1, free);
01994 }
01995 
01999 static void reverseFunc(sqlite3_context * context,
02000                 int argc, sqlite3_value ** argv)
02001 {
02002     const char *z;
02003     const char *zt;
02004     char *rz;
02005     char *rzt;
02006     size_t l;
02007     int i;
02008 
02009 assert(argc == 1);
02010     if (SQLITE_NULL == sqlite3_value_type(argv[0])) {
02011         sqlite3_result_null(context);
02012         return;
02013     }
02014     z = (const char *)sqlite3_value_text(argv[0]);
02015     l = strlen(z);
02016     rz = xmalloc(l + 1);
02017     rzt = rz + l;
02018     *(rzt--) = '\0';
02019 
02020     zt = z;
02021     while (sqliteCharVal(zt) != 0) {
02022         z = zt;
02023         sqliteNextChar(zt);
02024         for (i = 1; zt - i >= z; ++i)
02025             *(rzt--) = *(zt - i);
02026     }
02027 
02028     sqlite3_result_text(context, rz, -1, free);
02029 }
02030 
02031 #ifdef  NOTYET          /* XXX needs the sqlite3 map function */
02032 
02038 typedef struct StdevCtx StdevCtx;
02039 struct StdevCtx {
02040     double rM;
02041     double rS;
02042     int64_t cnt;                        /* number of elements */
02043 };
02044 
02053 typedef struct ModeCtx ModeCtx;
02054 struct ModeCtx {
02055     int64_t riM;                /* integer value found so far */
02056     double rdM;                 /* double value found so far */
02057     int64_t cnt;                /* number of elements so far */
02058     double pcnt;                /* number of elements smaller than a percentile */
02059     int64_t mcnt;               /* maximum number of occurrences (for mode) */
02060     int64_t mn;                 /* number of occurrences (for mode and percentiles) */
02061     int64_t is_double;          /* whether the computation is being done for doubles (>0) or integers (=0) */
02062     map *m;                     /* map structure used for the computation */
02063     int done;                   /* whether the answer has been found */
02064 };
02065 
02069 static void varianceStep(sqlite3_context * context,
02070                 int argc, sqlite3_value ** argv)
02071 {
02072     StdevCtx *p;
02073     double delta;
02074     double x;
02075 
02076 assert(argc == 1);
02077     p = sqlite3_aggregate_context(context, sizeof(*p));
02078     /* only consider non-null values */
02079     if (SQLITE_NULL != sqlite3_value_numeric_type(argv[0])) {
02080         p->cnt++;
02081         x = sqlite3_value_double(argv[0]);
02082         delta = (x - p->rM);
02083         p->rM += delta / p->cnt;
02084         p->rS += delta * (x - p->rM);
02085     }
02086 }
02087 
02091 static void modeStep(sqlite3_context * context,
02092                 int argc, sqlite3_value ** argv)
02093 {
02094     ModeCtx *p;
02095     int64_t xi = 0;
02096     double xd = 0.0;
02097     int64_t *iptr;
02098     double *dptr;
02099     int type;
02100 
02101 assert(argc == 1);
02102     type = sqlite3_value_numeric_type(argv[0]);
02103 
02104     if (type == SQLITE_NULL)
02105         return;
02106 
02107     p = sqlite3_aggregate_context(context, sizeof(*p));
02108 
02109     if (0 == (p->m)) {
02110         p->m = calloc(1, sizeof(map));
02111         if (type == SQLITE_INTEGER) {
02112             /* map will be used for integers */
02113             *(p->m) = map_make(int_cmp);
02114             p->is_double = 0;
02115         } else {
02116             p->is_double = 1;
02117             /* map will be used for doubles */
02118             *(p->m) = map_make(double_cmp);
02119         }
02120     }
02121 
02122     ++(p->cnt);
02123 
02124     if (0 == p->is_double) {
02125         xi = sqlite3_value_int64(argv[0]);
02126         iptr = (int64_t *) calloc(1, sizeof(int64_t));
02127         *iptr = xi;
02128         map_insert(p->m, iptr);
02129     } else {
02130         xd = sqlite3_value_double(argv[0]);
02131         dptr = (double *) calloc(1, sizeof(double));
02132         *dptr = xd;
02133         map_insert(p->m, dptr);
02134     }
02135 }
02136 
02141 static void modeIterate(void *e, int64_t c, void *pp)
02142 {
02143     int64_t ei;
02144     double ed;
02145     ModeCtx *p = (ModeCtx *) pp;
02146 
02147     if (0 == p->is_double) {
02148         ei = *(int *) (e);
02149 
02150         if (p->mcnt == c) {
02151             ++p->mn;
02152         } else if (p->mcnt < c) {
02153             p->riM = ei;
02154             p->mcnt = c;
02155             p->mn = 1;
02156         }
02157     } else {
02158         ed = *(double *) (e);
02159 
02160         if (p->mcnt == c) {
02161             ++p->mn;
02162         } else if (p->mcnt < c) {
02163             p->rdM = ed;
02164             p->mcnt = c;
02165             p->mn = 1;
02166         }
02167     }
02168 }
02169 
02175 static void medianIterate(void *e, int64_t c, void *pp)
02176 {
02177     int64_t ei;
02178     double ed;
02179     double iL;
02180     double iR;
02181     int il;
02182     int ir;
02183     ModeCtx *p = (ModeCtx *) pp;
02184 
02185     if (p->done > 0)
02186         return;
02187 
02188     iL = p->pcnt;
02189     iR = p->cnt - p->pcnt;
02190     il = p->mcnt + c;
02191     ir = p->cnt - p->mcnt;
02192 
02193     if (il >= iL) {
02194         if (ir >= iR) {
02195             ++p->mn;
02196             if (0 == p->is_double) {
02197                 ei = *(int *) (e);
02198                 p->riM += ei;
02199             } else {
02200                 ed = *(double *) (e);
02201                 p->rdM += ed;
02202             }
02203         } else {
02204             p->done = 1;
02205         }
02206     }
02207     p->mcnt += c;
02208 }
02209 
02213 static void modeFinalize(sqlite3_context * context)
02214 {
02215     ModeCtx *p = sqlite3_aggregate_context(context, 0);
02216     if (p && p->m) {
02217         map_iterate(p->m, modeIterate, p);
02218         map_destroy(p->m);
02219         free(p->m);
02220 
02221         if (1 == p->mn) {
02222             if (0 == p->is_double)
02223                 sqlite3_result_int64(context, p->riM);
02224             else
02225                 sqlite3_result_double(context, p->rdM);
02226         }
02227     }
02228 }
02229 
02233 static void _medianFinalize(sqlite3_context * context)
02234 {
02235     ModeCtx *p = (ModeCtx *) sqlite3_aggregate_context(context, 0);
02236     if (p && p->m) {
02237         p->done = 0;
02238         map_iterate(p->m, medianIterate, p);
02239         map_destroy(p->m);
02240         free(p->m);
02241 
02242         if (0 == p->is_double)
02243             if (1 == p->mn)
02244                 sqlite3_result_int64(context, p->riM);
02245             else
02246                 sqlite3_result_double(context, p->riM * 1.0 / p->mn);
02247         else
02248             sqlite3_result_double(context, p->rdM / p->mn);
02249     }
02250 }
02251 
02255 static void medianFinalize(sqlite3_context * context)
02256 {
02257     ModeCtx *p = (ModeCtx *) sqlite3_aggregate_context(context, 0);
02258     if (p != NULL) {
02259         p->pcnt = (p->cnt) / 2.0;
02260         _medianFinalize(context);
02261     }
02262 }
02263 
02267 static void lower_quartileFinalize(sqlite3_context * context)
02268 {
02269     ModeCtx *p = (ModeCtx *) sqlite3_aggregate_context(context, 0);
02270     if (p != NULL) {
02271         p->pcnt = (p->cnt) / 4.0;
02272         _medianFinalize(context);
02273     }
02274 }
02275 
02279 static void upper_quartileFinalize(sqlite3_context * context)
02280 {
02281     ModeCtx *p = (ModeCtx *) sqlite3_aggregate_context(context, 0);
02282     if (p != NULL) {
02283         p->pcnt = (p->cnt) * 3 / 4.0;
02284         _medianFinalize(context);
02285     }
02286 }
02287 
02291 static void stdevFinalize(sqlite3_context * context)
02292 {
02293     StdevCtx *p = sqlite3_aggregate_context(context, 0);
02294     if (p && p->cnt > 1)
02295         sqlite3_result_double(context, sqrt(p->rS / (p->cnt - 1)));
02296     else
02297         sqlite3_result_double(context, 0.0);
02298 }
02299 
02303 static void varianceFinalize(sqlite3_context * context)
02304 {
02305     StdevCtx *p = sqlite3_aggregate_context(context, 0);
02306     if (p && p->cnt > 1)
02307         sqlite3_result_double(context, p->rS / (p->cnt - 1));
02308     else
02309         sqlite3_result_double(context, 0.0);
02310 }
02311 #endif
02312 
02316 static void expandFunc(sqlite3_context * context,
02317                 int argc, sqlite3_value ** argv)
02318 {
02319     sqlite3_result_text(context,
02320         rpmExpand((const char *)sqlite3_value_text(argv[0]), NULL), -1, free);
02321 }
02322 
02326 static void regexpFunc(sqlite3_context* context,
02327                 int argc, sqlite3_value** argv)
02328 {
02329     const char * value = (const char *) sqlite3_value_text(argv[0]);
02330     const char * pattern = (const char *) sqlite3_value_text(argv[1]);
02331     miRE mire = mireNew(RPMMIRE_REGEX, 0);
02332     int rc = mireRegcomp(mire, pattern);
02333 
02334     rc = mireRegexec(mire, value, strlen(value));
02335     switch (rc) {
02336     case 0:
02337     case 1:
02338         sqlite3_result_int(context, rc);
02339         break;
02340     default:
02341         sqlite3_result_error(context, "invalid pattern", -1);
02342         break;
02343     }
02344     mire = mireFree(mire);
02345 }
02346 
02347 typedef struct rpmsqlCF_s * rpmsqlCF;
02348 struct rpmsqlCF_s {
02349     const char * zName;
02350     int8_t nArg;
02351     uint8_t argType;            /* 0: none.  1: db  2: (-1) */
02352     uint8_t eTextRep;           /* SQLITE_UTF8 or SQLITE_UTF16 */
02353     uint8_t  needCollSeq;
02354     void (*xFunc)  (sqlite3_context *, int, sqlite3_value **);
02355     void (*xStep)  (sqlite3_context *, int, sqlite3_value **);
02356     void (*xFinal) (sqlite3_context *);
02357 };
02358 
02359 static struct rpmsqlCF_s __CF[] = {
02360     /* math.h extensions */
02361   { "acos",             1, 0, SQLITE_UTF8,      0, acosFunc, NULL, NULL },
02362   { "asin",             1, 0, SQLITE_UTF8,      0, asinFunc, NULL, NULL },
02363   { "atan",             1, 0, SQLITE_UTF8,      0, atanFunc, NULL, NULL },
02364   { "atn2",             2, 0, SQLITE_UTF8,      0, atn2Func, NULL, NULL },
02365     /* XXX alias */
02366   { "atan2",            2, 0, SQLITE_UTF8,      0, atn2Func, NULL, NULL },
02367   { "acosh",            1, 0, SQLITE_UTF8,      0, acoshFunc, NULL, NULL },
02368   { "asinh",            1, 0, SQLITE_UTF8,      0, asinhFunc, NULL, NULL },
02369   { "atanh",            1, 0, SQLITE_UTF8,      0, atanhFunc, NULL, NULL },
02370 
02371 #ifdef  NOTYET
02372   { "difference",       2, 0, SQLITE_UTF8,      0, differenceFunc, NULL, NULL },
02373 #endif
02374   { "degrees",          1, 0, SQLITE_UTF8,      0, rad2degFunc, NULL, NULL },
02375   { "radians",          1, 0, SQLITE_UTF8,      0, deg2radFunc, NULL, NULL },
02376 
02377   { "cos",              1, 0, SQLITE_UTF8,      0, cosFunc, NULL, NULL },
02378   { "sin",              1, 0, SQLITE_UTF8,      0, sinFunc, NULL, NULL },
02379   { "tan",              1, 0, SQLITE_UTF8,      0, tanFunc, NULL, NULL },
02380   { "cot",              1, 0, SQLITE_UTF8,      0, cotFunc, NULL, NULL },
02381   { "cosh",             1, 0, SQLITE_UTF8,      0, coshFunc, NULL, NULL },
02382   { "sinh",             1, 0, SQLITE_UTF8,      0, sinhFunc, NULL, NULL },
02383   { "tanh",             1, 0, SQLITE_UTF8,      0, tanhFunc, NULL, NULL },
02384   { "coth",             1, 0, SQLITE_UTF8,      0, cothFunc, NULL, NULL },
02385 
02386   { "exp",              1, 0, SQLITE_UTF8,      0, expFunc, NULL, NULL },
02387   { "log",              1, 0, SQLITE_UTF8,      0, logFunc, NULL, NULL },
02388   { "log10",            1, 0, SQLITE_UTF8,      0, log10Func, NULL, NULL },
02389   { "power",            2, 0, SQLITE_UTF8,      0, powerFunc, NULL, NULL },
02390   { "sign",             1, 0, SQLITE_UTF8,      0, signFunc, NULL, NULL },
02391   { "sqrt",             1, 0, SQLITE_UTF8,      0, sqrtFunc, NULL, NULL },
02392   { "square",           1, 0, SQLITE_UTF8,      0, squareFunc, NULL, NULL },
02393 
02394   { "ceil",             1, 0, SQLITE_UTF8,      0, ceilFunc, NULL, NULL },
02395   { "floor",            1, 0, SQLITE_UTF8,      0, floorFunc, NULL, NULL },
02396 
02397   { "pi",               0, 0, SQLITE_UTF8,      1, piFunc, NULL, NULL },
02398 
02399     /* string extensions */
02400   { "replicate",        2, 0, SQLITE_UTF8,      0, replicateFunc, NULL, NULL },
02401   { "charindex",        2, 0, SQLITE_UTF8,      0, charindexFunc, NULL, NULL },
02402   { "charindex",        3, 0, SQLITE_UTF8,      0, charindexFunc, NULL, NULL },
02403   { "leftstr",          2, 0, SQLITE_UTF8,      0, leftFunc, NULL, NULL },
02404   { "rightstr",         2, 0, SQLITE_UTF8,      0, rightFunc, NULL, NULL },
02405   { "ltrim",            1, 0, SQLITE_UTF8,      0, ltrimFunc, NULL, NULL },
02406   { "rtrim",            1, 0, SQLITE_UTF8,      0, rtrimFunc, NULL, NULL },
02407   { "trim",             1, 0, SQLITE_UTF8,      0, trimFunc, NULL, NULL },
02408   { "replace",          3, 0, SQLITE_UTF8,      0, replaceFunc, NULL, NULL },
02409   { "reverse",          1, 0, SQLITE_UTF8,      0, reverseFunc, NULL, NULL },
02410   { "proper",           1, 0, SQLITE_UTF8,      0, properFunc, NULL, NULL },
02411 #ifdef  NOTYET  /* XXX figger multibyte char's. */
02412   { "padl",             2, 0, SQLITE_UTF8,      0, padlFunc, NULL, NULL },
02413   { "padr",             2, 0, SQLITE_UTF8,      0, padrFunc, NULL, NULL },
02414   { "padc",             2, 0, SQLITE_UTF8,      0, padcFunc, NULL, NULL },
02415 #endif
02416   { "strfilter",        2, 0, SQLITE_UTF8,      0, strfilterFunc, NULL, NULL },
02417 
02418     /* statistical aggregate extensions */
02419 #ifdef  NOTYET          /* XXX needs the sqlite3 map function */
02420   { "stdev",            1, 0, SQLITE_UTF8,      0, NULL, varianceStep, stdevFinalize  },
02421   { "variance",         1, 0, SQLITE_UTF8,      0, NULL, varianceStep, varianceFinalize  },
02422   { "mode",             1, 0, SQLITE_UTF8,      0, NULL, modeStep, modeFinalize },
02423   { "median",           1, 0, SQLITE_UTF8,      0, NULL, modeStep, medianFinalize },
02424   { "lower_quartile",   1, 0, SQLITE_UTF8,      0, NULL, modeStep, lower_quartileFinalize },
02425   { "upper_quartile",   1, 0, SQLITE_UTF8,      0, NULL, modeStep, upper_quartileFinalize },
02426 #endif
02427 
02428     /* RPM extensions. */
02429   { "expand",           1, 0, SQLITE_UTF8,      0, expandFunc, NULL, NULL },
02430   { "regexp",           2, 0, SQLITE_UTF8,      0, regexpFunc, NULL, NULL },
02431   { NULL,               0, 0, 0,                0, NULL, NULL, NULL }
02432 };
02433 static rpmsqlCF _CF = __CF;
02434 
02439 static int _rpmsqlLoadCF(rpmsql sql)
02440 {
02441     sqlite3 * db = (sqlite3 *)sql->I;
02442     rpmsqlCF CF;
02443     int rc = 0;
02444 
02445 SQLDBG((stderr, "--> %s(%p)\n", __FUNCTION__, sql));
02446     for (CF = _CF; CF->zName != NULL; CF++) {
02447         void * _pApp = NULL;
02448         int xx;
02449 
02450         switch (CF->argType) {
02451         default:
02452         case 0:  _pApp = NULL;          break;
02453         case 1:  _pApp = (void *)db;    break;
02454         case 2:  _pApp = (void *)-1;    break;
02455         }
02456 
02457         xx = rpmsqlCmd(sql, "create_function", db,
02458                 sqlite3_create_function(db, CF->zName, CF->nArg, CF->eTextRep,
02459                         _pApp,  CF->xFunc, CF->xStep, CF->xFinal));
02460 SQLDBG((stderr, "\t%s(%s) xx %d\n", "sqlite3_create_function", CF->zName, xx));
02461         if (xx && rc == 0)
02462             rc = xx;
02463 
02464 #ifdef  NOTYET
02465         if (CF->needColSeq) {
02466             FuncDef *pFunc = sqlite3FindFunction(db, CF->zName,
02467                                 strlen(CF_>zName), CF->nArg, CF->eTextRep, 0);
02468             if (pFunc) pFunc->needCollSeq = 1;
02469         }
02470 #endif
02471 
02472     }
02473 SQLDBG((stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, sql, rc));
02474     return rc;
02475 }
02476 
02477 /*==============================================================*/
02478 
02479 static struct rpmvd_s _envVD = {
02480         .split  = "=",
02481         .parse  = "key=val",
02482         .regex  = "^([^=]+)=(.*)$",
02483         .idx    = 1,
02484 };
02485 
02486 static int envCreateConnect(void * _db, void * pAux,
02487                 int argc, const char *const * argv,
02488                 rpmvt * vtp, char ** pzErr)
02489 {
02490     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_envVD), vtp);
02491 }
02492 
02493 struct sqlite3_module envModule = {
02494     .xCreate    = (void *) envCreateConnect,
02495     .xConnect   = (void *) envCreateConnect,
02496 };
02497 
02498 /*==============================================================*/
02499 
02500 static struct rpmvd_s _grdbVD = {
02501         .prefix = "%{?_etc_group}%{!?_etc_group:/etc/group}",
02502         .split  = ":",
02503         /* XXX "group" is a reserved keyword. */
02504         .parse  = "_group:passwd:gid:groups",
02505         .regex  = "^([^:]*):([^:]*):([^:]*):([^:]*)$",
02506         .idx    = 3,
02507 };
02508 
02509 static int grdbCreateConnect(void * _db, void * pAux,
02510                 int argc, const char *const * argv,
02511                 rpmvt * vtp, char ** pzErr)
02512 {
02513     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_grdbVD), vtp);
02514 }
02515 
02516 struct sqlite3_module grdbModule = {
02517     .xCreate    = (void *) grdbCreateConnect,
02518     .xConnect   = (void *) grdbCreateConnect,
02519 };
02520 
02521 /*==============================================================*/
02522 
02523 static struct rpmvd_s _procdbVD = {
02524         .prefix = "%{?_procdb}%{!?_procdb:/proc/[0-9]}",
02525         .split  = "/-",
02526         .parse  = "dir/pid/*",
02527         .regex  = "^(.+/)([0-9]+)$",
02528         .idx    = 2,
02529 };
02530 
02531 static int procdbCreateConnect(void * _db, void * pAux,
02532                 int argc, const char *const * argv,
02533                 rpmvt * vtp, char ** pzErr)
02534 {
02535     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_procdbVD), vtp);
02536 }
02537 
02538 struct sqlite3_module procdbModule = {
02539     .xCreate    = (void *) procdbCreateConnect,
02540     .xConnect   = (void *) procdbCreateConnect,
02541 };
02542 
02543 /*==============================================================*/
02544 
02545 static struct rpmvd_s _pwdbVD = {
02546         .prefix = "%{?_etc_passwd}%{!?_etc_passwd:/etc/passwd}",
02547         .split  = ":",
02548         .parse  = "user:passwd:uid:gid:gecos:dir:shell",
02549         .regex  = "^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)$",
02550         .idx    = 3,
02551 };
02552 
02553 static int pwdbCreateConnect(void * _db, void * pAux,
02554                 int argc, const char *const * argv,
02555                 rpmvt * vtp, char ** pzErr)
02556 {
02557     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_pwdbVD), vtp);
02558 }
02559 
02560 struct sqlite3_module pwdbModule = {
02561     .xCreate    = (void *) pwdbCreateConnect,
02562     .xConnect   = (void *) pwdbCreateConnect,
02563 };
02564 
02565 /*==============================================================*/
02566 
02567 static struct rpmvd_s _repodbVD = {
02568         /* XXX where to map the default? */
02569         .prefix = "%{?_repodb}%{!?_repodb:/X/popt/}",
02570         .split  = "/-.",
02571         .parse  = "dir/file-NVRA-N-V-R.A",
02572         .regex  = "^(.+/)(((.*)-([^-]+)-([^-]+)\\.([^.]+))\\.rpm)$",
02573         .idx    = 2,
02574 };
02575 
02576 static int repodbCreateConnect(void * _db, void * pAux,
02577                 int argc, const char *const * argv,
02578                 rpmvt * vtp, char ** pzErr)
02579 {
02580     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_repodbVD), vtp);
02581 }
02582 
02583 struct sqlite3_module repodbModule = {
02584     .xCreate    = (void *) repodbCreateConnect,
02585     .xConnect   = (void *) repodbCreateConnect,
02586 };
02587 
02588 /*==============================================================*/
02589 
02590 static int _stat_debug = 0;
02591 
02592 static struct rpmvd_s _statVD = {
02593         .split  = " ,",
02594         .parse  = "st_dev,st_ino,st_mode,st_nlink,st_uid,st_gid,st_rdev,st_size,st_blksize,st_blocks,st_atime,st_mtime,st_ctime",
02595 };
02596 
02597 static int statCreateConnect(void * _db, void * pAux,
02598                 int argc, const char *const * argv,
02599                 rpmvt * vtp, char ** pzErr)
02600 {
02601     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_statVD), vtp);
02602 }
02603 
02604 static int statColumn(rpmvc vc, void * _pContext, int colx)
02605 {
02606     sqlite3_context * pContext = (sqlite3_context *) _pContext;
02607     rpmvt vt = vc->vt;
02608     const char * path = vt->av[vc->ix];
02609     const char * col = vt->cols[colx];
02610     struct stat sb, *st = &sb;  /* XXX move to rpmvcNext for performance */
02611     int ret = Lstat(path, &sb);
02612     int rc = SQLITE_OK;
02613 
02614 if (_stat_debug < 0)
02615 fprintf(stderr, "--> %s(%p,%p,%d)\n", __FUNCTION__, vc, pContext, colx);
02616 
02617 
02618     if (!strcmp(col, "path"))
02619         sqlite3_result_text(pContext, path, -1, SQLITE_STATIC);
02620     else if (!strcmp(col, "st_dev") && !ret)
02621         sqlite3_result_int64(pContext, st->st_dev);
02622     else if (!strcmp(col, "st_ino") && !ret)
02623         sqlite3_result_int64(pContext, st->st_ino);
02624     else if (!strcmp(col, "st_mode") && !ret)
02625         sqlite3_result_int64(pContext, st->st_mode);
02626     else if (!strcmp(col, "st_nlink") && !ret)
02627         sqlite3_result_int64(pContext, st->st_nlink);
02628     else if (!strcmp(col, "st_uid") && !ret)
02629         sqlite3_result_int64(pContext, st->st_uid);
02630     else if (!strcmp(col, "st_gid") && !ret)
02631         sqlite3_result_int64(pContext, st->st_gid);
02632     else if (!strcmp(col, "st_rdev") && !ret)
02633         sqlite3_result_int64(pContext, st->st_rdev);
02634     else if (!strcmp(col, "st_size") && !ret)
02635         sqlite3_result_int64(pContext, st->st_size);
02636     else if (!strcmp(col, "st_blksize") && !ret)
02637         sqlite3_result_int64(pContext, st->st_blksize);
02638     else if (!strcmp(col, "st_blocks") && !ret)
02639         sqlite3_result_int64(pContext, st->st_blocks);
02640     else if (!strcmp(col, "st_atime") && !ret)
02641         sqlite3_result_int64(pContext, st->st_atime);
02642     else if (!strcmp(col, "st_mtime") && !ret)
02643         sqlite3_result_int64(pContext, st->st_mtime);
02644     else if (!strcmp(col, "st_ctime") && !ret)
02645         sqlite3_result_int64(pContext, st->st_ctime);
02646         /* XXX pick up *BSD derangements */
02647     else
02648         sqlite3_result_null(pContext);
02649 
02650 if (_stat_debug < 0)
02651 fprintf(stderr, "<-- %s(%p,%p,%d) rc %d\n", __FUNCTION__, vc, pContext, colx, rc);
02652 
02653     return rc;
02654 }
02655 
02656 struct sqlite3_module statModule = {
02657     .xCreate    = (void *) statCreateConnect,
02658     .xConnect   = (void *) statCreateConnect,
02659     .xColumn    = (void *) statColumn,
02660 };
02661 
02662 /*==============================================================*/
02663 
02664 static struct rpmvd_s _yumdbVD = {
02665         .prefix = "%{?_yumdb}%{!?_yumdb:/var/lib/yum/yumdb}/",
02666         .split  = "/-",
02667         .parse  = "dir/hash-NVRA-N-V-R-A/*",
02668         .regex  = "^(.+/)([^-]+)-((.*)-([^-]+)-([^-]+)-([^-]+))$",
02669         .idx    = 2,
02670 };
02671 
02672 static int yumdbCreateConnect(void * _db, void * pAux,
02673                 int argc, const char *const * argv,
02674                 rpmvt * vtp, char ** pzErr)
02675 {
02676     return rpmvtLoadArgv(rpmvtNew(_db, pAux, argv, &_yumdbVD), vtp);
02677 }
02678 
02679 struct sqlite3_module yumdbModule = {
02680     .xCreate    = (void *) yumdbCreateConnect,
02681     .xConnect   = (void *) yumdbCreateConnect,
02682 };
02683 
02684 /*==============================================================*/
02685 
02686 struct sqlite3_module _rpmvmTemplate = {
02687     .xCreate    = (void *) rpmvtCreate,
02688     .xConnect   = (void *) rpmvtConnect,
02689     .xBestIndex = (void *) rpmvtBestIndex,
02690     .xDisconnect = (void *) rpmvtDisconnect,
02691     .xDestroy   = (void *) rpmvtDestroy,
02692     .xOpen      = (void *) rpmvcOpen,
02693     .xClose     = (void *) rpmvcClose,
02694     .xFilter    = (void *) rpmvcFilter,
02695     .xNext      = (void *) rpmvcNext,
02696     .xEof       = (void *) rpmvcEof,
02697     .xColumn    = (void *) rpmvcColumn,
02698     .xRowid     = (void *) rpmvcRowid,
02699     .xUpdate    = (void *) rpmvtUpdate,
02700     .xBegin     = (void *) rpmvtBegin,
02701     .xSync      = (void *) rpmvtSync,
02702     .xCommit    = (void *) rpmvtCommit,
02703     .xRollback  = (void *) rpmvtRollback,
02704     .xFindFunction = (void *) rpmvtFindFunction,
02705     .xRename    = (void *) rpmvtRename
02706 };
02707 
02708 static struct rpmsqlVMT_s __VMT[] = {
02709   { "Argv",             NULL, NULL },
02710   { "Env",              &envModule, NULL },
02711   { "Grdb",             &grdbModule, NULL },
02712   { "Procdb",           &procdbModule, NULL },
02713   { "Pwdb",             &pwdbModule, NULL },
02714   { "Repodb",           &repodbModule, NULL },
02715   { "Stat",             &statModule, NULL },
02716   { "Yumdb",            &yumdbModule, NULL },
02717   { NULL,               NULL, NULL }
02718 };
02719 
02720 static void rpmsqlVMFree(void * _VM)
02721         /*@*/
02722 {
02723 SQLDBG((stderr, "--> %s(%p)\n", __FUNCTION__, _VM));
02724     if (_VM)
02725         free(_VM);
02726 }
02727 
02728 #ifdef  UNUSED
02729 static void dumpVM(const char * msg, const rpmsqlVM s)
02730 {
02731 fprintf(stderr, "--------------------- %s\n", (msg ? msg : ""));
02732 #define VMPRT(f) if (s->f) fprintf(stderr, "%20s: %p\n", #f, s->f)
02733         VMPRT(xCreate);
02734         VMPRT(xConnect);
02735         VMPRT(xBestIndex);
02736         VMPRT(xDisconnect);
02737         VMPRT(xDestroy);
02738         VMPRT(xOpen);
02739         VMPRT(xClose);
02740         VMPRT(xFilter);
02741         VMPRT(xNext);
02742         VMPRT(xEof);
02743         VMPRT(xColumn);
02744         VMPRT(xRowid);
02745         VMPRT(xUpdate);
02746         VMPRT(xBegin);
02747         VMPRT(xSync);
02748         VMPRT(xCommit);
02749         VMPRT(xRollback);
02750         VMPRT(xFindFunction);
02751         VMPRT(xRename);
02752 #undef  VMPRT
02753 }
02754 #endif
02755 
02756 static /*@only@*/ rpmsqlVM rpmsqlVMNew(/*@null@*/ const rpmsqlVM s)
02757 {
02758     rpmsqlVM t = xcalloc(1, sizeof(*t));
02759 
02760 SQLDBG((stderr, "--> %s(%p)\n", __FUNCTION__, s));
02761     *t = _rpmvmTemplate;        /* structure assignment */
02762 
02763     if (s) {
02764         if (s->iVersion) t->iVersion = s->iVersion;
02765 #define VMCPY(f) if (s->f) t->f = ((s->f != (void *)-1) ? s->f : NULL)
02766         VMCPY(xCreate);
02767         VMCPY(xConnect);
02768         VMCPY(xBestIndex);
02769         VMCPY(xDisconnect);
02770         VMCPY(xDestroy);
02771         VMCPY(xOpen);
02772         VMCPY(xClose);
02773         VMCPY(xFilter);
02774         VMCPY(xNext);
02775         VMCPY(xEof);
02776         VMCPY(xColumn);
02777         VMCPY(xRowid);
02778         VMCPY(xUpdate);
02779         VMCPY(xBegin);
02780         VMCPY(xSync);
02781         VMCPY(xCommit);
02782         VMCPY(xRollback);
02783         VMCPY(xFindFunction);
02784         VMCPY(xRename);
02785 #undef  VMCPY
02786     }
02787 SQLDBG((stderr, "<-- %s(%p) %p\n", __FUNCTION__, s, t));
02788     return t;
02789 }
02790 
02791 int _rpmsqlLoadVMT(void * _db, const rpmsqlVMT _VMT)
02792 {
02793     sqlite3 * db = (sqlite3 *) _db;
02794     rpmsqlVMT VMT;
02795     int rc = 0;
02796 
02797 SQLDBG((stderr, "--> %s(%p,%p)\n", __FUNCTION__, _db, _VMT));
02798     for (VMT = (rpmsqlVMT)_VMT; VMT->zName != NULL; VMT++) {
02799         int xx;
02800 
02801         xx = rpmsqlCmd(_rpmsqlI, "create_module_v2", db,
02802                 sqlite3_create_module_v2(db, VMT->zName,
02803                         rpmsqlVMNew(VMT->module), VMT->data, rpmsqlVMFree));
02804 SQLDBG((stderr, "\t%s(%s) xx %d\n", "sqlite3_create_module_v2", VMT->zName, xx));
02805         if (xx && rc == 0)
02806             rc = xx;
02807 
02808     }
02809 SQLDBG((stderr, "<-- %s(%p,%p) rc %d\n", __FUNCTION__, _db, _VMT, rc));
02810     return rc;
02811 }
02812 
02813 /*==============================================================*/
02814 /* XXX HACK: AWOL in -lsqlite3 on CM14 */
02815 #if SQLITE_VERSION_NUMBER <= 3006015
02816 #define sqlite3_enable_load_extension(db, onoff)                SQLITE_OK
02817 #define sqlite3_load_extension(db, zFile, zProc, pzErrMsg)      SQLITE_OK
02818 #endif
02819 
02825 static int _rpmsqlOpenDB(rpmsql sql)
02826 {
02827     int rc = -1;        /* assume failure */
02828     sqlite3 * db;
02829 
02830 assert(sql);
02831 
02832     db = (sqlite3 *)sql->I;
02833     if (db == NULL) {
02834         int rc;
02835         rc = rpmsqlCmd(sql, "open", db,         /* XXX watchout: arg order */
02836                 sqlite3_open(sql->zDbFilename, &db));
02837         sql->I = db;
02838 
02839         if (db && rc == SQLITE_OK) {
02840             (void) _rpmsqlLoadCF(sql);
02841             (void) _rpmsqlLoadVMT(db, __VMT);
02842         }
02843 
02844         if (db == NULL || sqlite3_errcode(db) != SQLITE_OK) {
02845             /* XXX rpmlog */
02846             rpmsql_error(1, _("unable to open database \"%s\": %s"),
02847                     sql->zDbFilename, sqlite3_errmsg(db));
02848             goto exit;
02849         }
02850         /* Enable extension loading (if not disabled). */
02851         if (!F_ISSET(sql, NOLOAD))
02852             (void) rpmsqlCmd(sql, "enable_load_extension", db,
02853                         sqlite3_enable_load_extension(db, 1));
02854     }
02855     rc = 0;
02856 
02857 exit:
02858 SQLDBG((stderr, "<-- %s(%p) rc %d %s\n", __FUNCTION__, sql, rc, sql->zDbFilename));
02859     return rc;
02860 }
02861 
02862 #endif  /* defined(WITH_SQLITE) */
02863 
02864 /*==============================================================*/
02865 
02866 #if defined(WITH_SQLITE)
02867 
02870 static int isNumber(const char *z, int *realnum)
02871 {
02872     if (*z == '-' || *z == '+')
02873         z++;
02874     if (!isdigit(*z))
02875         return 0;
02876     z++;
02877     if (realnum)
02878         *realnum = 0;
02879     while (isdigit(*z))
02880         z++;
02881     if (*z == '.') {
02882         z++;
02883         if (!isdigit(*z))
02884             return 0;
02885         while (isdigit(*z))
02886             z++;
02887         if (realnum)
02888             *realnum = 1;
02889     }
02890     if (*z == 'e' || *z == 'E') {
02891         z++;
02892         if (*z == '+' || *z == '-')
02893             z++;
02894         if (!isdigit(*z))
02895             return 0;
02896         while (isdigit(*z))
02897             z++;
02898         if (realnum)
02899             *realnum = 1;
02900     }
02901     return *z == 0;
02902 }
02903 
02908 static int strlen30(const char *z)
02909 {
02910     const char *z2 = z;
02911     while (*z2)
02912         z2++;
02913     return 0x3fffffff & (int) (z2 - z);
02914 }
02915 #endif  /* defined(WITH_SQLITE) */
02916 
02917 /*==============================================================*/
02918 #if defined(WITH_SQLITE)
02919 
02923 static void output_hex_blob(rpmsql sql, const void *pBlob, int nBlob)
02924 {
02925     char *zBlob = (char *) pBlob;
02926     int i;
02927 
02928 SQLDBG((stderr, "--> %s(%p,%p[%u])\n", __FUNCTION__, sql, pBlob, (unsigned)nBlob));
02929     rpmsqlFprintf(sql, "X'");
02930     for (i = 0; i < nBlob; i++)
02931         rpmsqlFprintf(sql, "%02x", zBlob[i]);
02932     rpmsqlFprintf(sql, "'");
02933 }
02934 
02939 static void output_quoted_string(rpmsql sql, const char *z)
02940 {
02941     int i;
02942     int nSingle = 0;
02943 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, z));
02944     for (i = 0; z[i]; i++) {
02945         if (z[i] == '\'')
02946             nSingle++;
02947     }
02948     if (nSingle == 0) {
02949         rpmsqlFprintf(sql, "'%s'", z);
02950     } else {
02951         rpmsqlFprintf(sql, "'");
02952         while (*z) {
02953             for (i = 0; z[i] && z[i] != '\''; i++)
02954                 ;
02955             if (i == 0) {
02956                 rpmsqlFprintf(sql, "''");
02957                 z++;
02958             } else if (z[i] == '\'') {
02959                 rpmsqlFprintf(sql, "%.*s''", i, z);
02960                 z += i + 1;
02961             } else {
02962                 rpmsqlFprintf(sql, "%s", z);
02963                 break;
02964             }
02965         }
02966         rpmsqlFprintf(sql, "'");
02967     }
02968 }
02969 
02974 static void output_c_string(rpmsql sql, const char *z)
02975 {
02976     unsigned int c;
02977 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, z));
02978     rpmsqlFprintf(sql, "\"");
02979     while ((c = *(z++)) != 0) {
02980         if (c == '\\')
02981             rpmsqlFprintf(sql, "\\\\");
02982         else if (c == '\t')
02983             rpmsqlFprintf(sql, "\\t");
02984         else if (c == '\n')
02985             rpmsqlFprintf(sql, "\\n");
02986         else if (c == '\r')
02987             rpmsqlFprintf(sql, "\\r");
02988         else if (!isprint(c))
02989             rpmsqlFprintf(sql, "\\%03o", c & 0xff);
02990         else
02991             rpmsqlFprintf(sql, "%c", c);
02992     }
02993     rpmsqlFprintf(sql, "\"");
02994 }
02995 
03001 static void output_html_string(rpmsql sql, const char *z)
03002 {
03003     int i;
03004 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, z));
03005     while (*z) {
03006         for (i = 0; z[i]
03007              && z[i] != '<'
03008              && z[i] != '&'
03009              && z[i] != '>' && z[i] != '\"' && z[i] != '\''; i++) {
03010         }
03011         if (i > 0)
03012             rpmsqlFprintf(sql, "%.*s", i, z);
03013         if (z[i] == '<')
03014             rpmsqlFprintf(sql, "&lt;");
03015         else if (z[i] == '&')
03016             rpmsqlFprintf(sql, "&amp;");
03017         else if (z[i] == '>')
03018             rpmsqlFprintf(sql, "&gt;");
03019         else if (z[i] == '\"')
03020             rpmsqlFprintf(sql, "&quot;");
03021         else if (z[i] == '\'')
03022             rpmsqlFprintf(sql, "&#39;");
03023         else
03024             break;
03025         z += i + 1;
03026     }
03027 }
03028 
03033 /*@unchecked@*/
03034 static const char needCsvQuote[] = {
03035     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03036     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03037     1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
03038     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
03039     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
03040     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
03041     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
03042     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
03043     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03044     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03045     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03046     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03047     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03048     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03049     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03050     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
03051 };
03052 
03060 static void output_csv(rpmsql sql, const char *z, int bSep)
03061 {
03062 SQLDBG((stderr, "--> %s(%p,%s,0x%x)\n", __FUNCTION__, sql, z, bSep));
03063     if (z == 0) {
03064         rpmsqlFprintf(sql, "%s", sql->nullvalue);
03065     } else {
03066         int i;
03067         int nSep = strlen30(sql->separator);
03068         for (i = 0; z[i]; i++) {
03069             if (needCsvQuote[((unsigned char *) z)[i]]
03070                 || (z[i] == sql->separator[0] &&
03071                     (nSep == 1 || memcmp(z, sql->separator, nSep) == 0))) {
03072                 i = 0;
03073                 break;
03074             }
03075         }
03076         if (i == 0) {
03077             rpmsqlFprintf(sql, "\"");
03078             for (i = 0; z[i]; i++) {
03079                 if (z[i] == '"')
03080                     rpmsqlFprintf(sql, "\"");
03081                 rpmsqlFprintf(sql, "%c", z[i]);
03082             }
03083             rpmsqlFprintf(sql, "\"");
03084         } else {
03085             rpmsqlFprintf(sql, "%s", z);
03086         }
03087     }
03088     if (bSep)
03089         rpmsqlFprintf(sql, "%s", sql->separator);
03090 }
03091 
03097 static int _rpmsqlShellCallback(void * _sql, int nArg, char **azArg, char **azCol,
03098                           int *aiType)
03099 {
03100     rpmsql sql = (rpmsql)  _sql;
03101     int w;
03102     int i;
03103 
03104 SQLDBG((stderr, "--> %s(%p,%d,%p,%p,%p)\n", __FUNCTION__,  _sql, nArg, azArg, azCol, aiType));
03105     switch (sql->mode) {
03106     case RPMSQL_MODE_LINE:
03107         w = 5;
03108         if (azArg == 0)
03109             break;
03110         for (i = 0; i < nArg; i++) {
03111             int len = strlen30(azCol[i] ? azCol[i] : "");
03112             if (len > w)
03113                 w = len;
03114         }
03115         if (sql->cnt++ > 0)
03116             rpmsqlFprintf(sql, "\n");
03117         for (i = 0; i < nArg; i++)
03118             rpmsqlFprintf(sql, "%*s = %s\n", w, azCol[i],
03119                         azArg[i] ? azArg[i] : sql->nullvalue);
03120         break;
03121     case RPMSQL_MODE_EXPLAIN:
03122     case RPMSQL_MODE_COLUMN:
03123         if (sql->cnt++ == 0) {
03124             for (i = 0; i < nArg; i++) {
03125                 int n;
03126                 w = (i < ArraySize(sql->colWidth) ? sql->colWidth[i] : 0);
03127 
03128                 if (w <= 0) {
03129                     w = strlen30(azCol[i] ? azCol[i] : "");
03130                     if (w < 10)
03131                         w = 10;
03132                     n = strlen30(azArg && azArg[i]
03133                                 ? azArg[i] : sql-> nullvalue);
03134                     if (w < n)
03135                         w = n;
03136                 }
03137                 if (i < ArraySize(sql->actualWidth))
03138                     sql->actualWidth[i] = w;
03139                 if (F_ISSET(sql, SHOWHDR)) {
03140                     rpmsqlFprintf(sql, "%-*.*s%s", w, w, azCol[i],
03141                                 i == nArg - 1 ? "\n" : "  ");
03142                 }
03143             }
03144             if (F_ISSET(sql, SHOWHDR)) {
03145                 for (i = 0; i < nArg; i++) {
03146                     w = (i < ArraySize(sql->actualWidth)
03147                                 ? sql->actualWidth[i] : 10);
03148 
03149                     rpmsqlFprintf(sql, "%-*.*s%s", w, w,
03150                 "-----------------------------------"
03151                 "----------------------------------------------------------",
03152                                 i == nArg - 1 ? "\n" : "  ");
03153                 }
03154             }
03155         }
03156         if (azArg == 0)
03157             break;
03158         for (i = 0; i < nArg; i++) {
03159             w = (i < ArraySize(sql->actualWidth) ? sql->actualWidth[i] : 10);
03160             if (sql->mode == RPMSQL_MODE_EXPLAIN && azArg[i] &&
03161                 strlen30(azArg[i]) > w) {
03162                 w = strlen30(azArg[i]);
03163             }
03164             rpmsqlFprintf(sql, "%-*.*s%s", w, w,
03165                         azArg[i] ? azArg[i] : sql->nullvalue,
03166                         i == nArg - 1 ? "\n" : "  ");
03167         }
03168         break;
03169     case RPMSQL_MODE_SEMI:
03170     case RPMSQL_MODE_LIST:
03171         if (sql->cnt++ == 0 && F_ISSET(sql, SHOWHDR)) {
03172             for (i = 0; i < nArg; i++)
03173                 rpmsqlFprintf(sql, "%s%s", azCol[i],
03174                             i == nArg - 1 ? "\n" : sql->separator);
03175         }
03176 
03177         if (azArg == 0)
03178             break;
03179         for (i = 0; i < nArg; i++) {
03180             char *z = azArg[i];
03181             if (z == 0)
03182                 z = sql->nullvalue;
03183             rpmsqlFprintf(sql, "%s", z);
03184             if (i < nArg - 1)
03185                 rpmsqlFprintf(sql, "%s", sql->separator);
03186             else if (sql->mode == RPMSQL_MODE_SEMI)
03187                 rpmsqlFprintf(sql, ";\n");
03188             else
03189                 rpmsqlFprintf(sql, "\n");
03190         }
03191         break;
03192     case RPMSQL_MODE_HTML:
03193         if (sql->cnt++ == 0 && F_ISSET(sql, SHOWHDR)) {
03194             rpmsqlFprintf(sql, "<TR>");
03195             for (i = 0; i < nArg; i++) {
03196                 rpmsqlFprintf(sql, "<TH>");
03197                 output_html_string(sql, azCol[i]);
03198                 rpmsqlFprintf(sql, "</TH>\n");
03199             }
03200             rpmsqlFprintf(sql, "</TR>\n");
03201         }
03202         if (azArg == 0)
03203             break;
03204         rpmsqlFprintf(sql, "<TR>");
03205         for (i = 0; i < nArg; i++) {
03206             rpmsqlFprintf(sql, "<TD>");
03207             output_html_string(sql, azArg[i] ? azArg[i] : sql->nullvalue);
03208             rpmsqlFprintf(sql, "</TD>\n");
03209         }
03210         rpmsqlFprintf(sql, "</TR>\n");
03211         break;
03212     case RPMSQL_MODE_TCL:
03213         if (sql->cnt++ == 0 && F_ISSET(sql, SHOWHDR)) {
03214             for (i = 0; i < nArg; i++) {
03215                 output_c_string(sql, azCol[i] ? azCol[i] : "");
03216                 rpmsqlFprintf(sql, "%s", sql->separator);
03217             }
03218             rpmsqlFprintf(sql, "\n");
03219         }
03220         if (azArg == 0)
03221             break;
03222         for (i = 0; i < nArg; i++) {
03223             output_c_string(sql, azArg[i] ? azArg[i] : sql->nullvalue);
03224             rpmsqlFprintf(sql, "%s", sql->separator);
03225         }
03226         rpmsqlFprintf(sql, "\n");
03227         break;
03228     case RPMSQL_MODE_CSV:
03229         if (sql->cnt++ == 0 && F_ISSET(sql, SHOWHDR)) {
03230             for (i = 0; i < nArg; i++)
03231                 output_csv(sql, azCol[i] ? azCol[i] : "", i < nArg - 1);
03232             rpmsqlFprintf(sql, "\n");
03233         }
03234         if (azArg == 0)
03235             break;
03236         for (i = 0; i < nArg; i++) 
03237             output_csv(sql, azArg[i], i < nArg - 1);
03238         rpmsqlFprintf(sql, "\n");
03239         break;
03240     case RPMSQL_MODE_INSERT:
03241         sql->cnt++;
03242         if (azArg == 0)
03243             break;
03244         rpmsqlFprintf(sql, "INSERT INTO %s VALUES(", sql->zDestTable);
03245         for (i = 0; i < nArg; i++) {
03246             char *zSep = i > 0 ? "," : "";
03247             if ((azArg[i] == 0) || (aiType && aiType[i] == SQLITE_NULL)) {
03248                 rpmsqlFprintf(sql, "%sNULL", zSep);
03249             } else if (aiType && aiType[i] == SQLITE_TEXT) {
03250                 if (zSep[0])
03251                     rpmsqlFprintf(sql, "%s", zSep);
03252                 output_quoted_string(sql, azArg[i]);
03253             } else if (aiType
03254                            && (aiType[i] == SQLITE_INTEGER
03255                                || aiType[i] == SQLITE_FLOAT)) {
03256                 rpmsqlFprintf(sql, "%s%s", zSep, azArg[i]);
03257             } else if (aiType && aiType[i] == SQLITE_BLOB && sql->S) {
03258                 sqlite3_stmt * pStmt = (sqlite3_stmt *)sql->S;
03259                 const void *pBlob = sqlite3_column_blob(pStmt, i);
03260                 int nBlob = sqlite3_column_bytes(pStmt, i);
03261                 if (zSep[0])
03262                     rpmsqlFprintf(sql, "%s", zSep);
03263                 output_hex_blob(sql, pBlob, nBlob);
03264             } else if (isNumber(azArg[i], 0)) {
03265                 rpmsqlFprintf(sql, "%s%s", zSep, azArg[i]);
03266             } else {
03267                 if (zSep[0])
03268                     rpmsqlFprintf(sql, "%s", zSep);
03269                 output_quoted_string(sql, azArg[i]);
03270             }
03271         }
03272         rpmsqlFprintf(sql, ");\n");
03273         break;
03274     }
03275     return 0;
03276 }
03277 
03283 static int callback(void *_sql, int nArg, char **azArg, char **azCol)
03284 {
03285     /* since we don't have type info, call the _rpmsqlShellCallback with a NULL value */
03286     return _rpmsqlShellCallback(_sql, nArg, azArg, azCol, NULL);
03287 }
03288 
03295 static void set_table_name(rpmsql sql, const char *zName)
03296 {
03297     int i, n;
03298     int needQuote;
03299     char *z;
03300 
03301 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, zName));
03302     sql->zDestTable = _free(sql->zDestTable);
03303     if (zName == NULL)
03304         return;
03305     needQuote = !xisalpha((unsigned char) *zName) && *zName != '_';
03306     for (i = n = 0; zName[i]; i++, n++) {
03307         if (!xisalnum((unsigned char) zName[i]) && zName[i] != '_') {
03308             needQuote = 1;
03309             if (zName[i] == '\'')
03310                 n++;
03311         }
03312     }
03313     if (needQuote)
03314         n += 2;
03315     sql->zDestTable = z = xmalloc(n + 1);
03316     n = 0;
03317     if (needQuote)
03318         z[n++] = '\'';
03319     for (i = 0; zName[i]; i++) {
03320         z[n++] = zName[i];
03321         if (zName[i] == '\'')
03322             z[n++] = '\'';
03323     }
03324     if (needQuote)
03325         z[n++] = '\'';
03326     z[n] = 0;
03327 }
03328 
03338 static char *appendText(char *zIn, char const *zAppend, char quote)
03339 {
03340     int len;
03341     int i;
03342     int nAppend = strlen30(zAppend);
03343     int nIn = (zIn ? strlen30(zIn) : 0);
03344 
03345 SQLDBG((stderr, "--> %s(%s,%s,0x%02x)\n", __FUNCTION__, zIn, zAppend, quote));
03346     len = nAppend + nIn + 1;
03347     if (quote) {
03348         len += 2;
03349         for (i = 0; i < nAppend; i++) {
03350             if (zAppend[i] == quote)
03351                 len++;
03352         }
03353     }
03354 
03355     zIn = (char *) xrealloc(zIn, len);
03356 
03357     if (quote) {
03358         char *zCsr = &zIn[nIn];
03359         *zCsr++ = quote;
03360         for (i = 0; i < nAppend; i++) {
03361             *zCsr++ = zAppend[i];
03362             if (zAppend[i] == quote)
03363                 *zCsr++ = quote;
03364         }
03365         *zCsr++ = quote;
03366         *zCsr++ = '\0';
03367 assert((zCsr - zIn) == len);
03368     } else {
03369         memcpy(&zIn[nIn], zAppend, nAppend);
03370         zIn[len - 1] = '\0';
03371     }
03372 
03373     return zIn;
03374 }
03375 
03376 
03385 static int run_table_dump_query(rpmsql sql, sqlite3 * db,
03386                                 const char *zSelect, const char *zFirstRow)
03387 {
03388     sqlite3_stmt * pSelect;
03389     int rc;
03390 SQLDBG((stderr, "--> %s(%p,%p,%s,%s)\n", __FUNCTION__, sql, db, zSelect, zFirstRow));
03391     rc = rpmsqlCmd(sql, "prepare", db,
03392         sqlite3_prepare(db, zSelect, -1, &pSelect, 0));
03393     if (rc || pSelect == NULL)
03394         return rc;
03395 
03396     while ((rc = rpmsqlCmd(sql, "step", db,
03397                 sqlite3_step(pSelect))) == SQLITE_ROW)
03398     {
03399         if (zFirstRow) {
03400             rpmsqlFprintf(sql, "%s", zFirstRow);
03401             zFirstRow = NULL;
03402         }
03403         rpmsqlFprintf(sql, "%s;\n", sqlite3_column_text(pSelect, 0));
03404     }
03405 
03406     return rpmsqlCmd(sql, "finalize", db,
03407         sqlite3_finalize(pSelect));
03408 }
03409 #endif  /* defined(WITH_SQLITE) */
03410 
03411 /*==============================================================*/
03412 
03413 #if defined(WITH_SQLITE)
03414 #define iseol(_c)       ((char)(_c) == '\n' || (char)(_c) == '\r')
03415 
03423 /*@null@*/
03424 static char *
03425 rpmsqlFgets(/*@returned@*/ char * buf, size_t nbuf, rpmsql sql)
03426         /*@globals fileSystem @*/
03427         /*@modifies buf, fileSystem @*/
03428 {
03429     FD_t ifd = sql->ifd;
03430 /* XXX sadly, fgets(3) cannot be used against a LIBIO wrapped .fpio FD_t */
03431 FILE * ifp = (!F_ISSET(sql, PROMPT) ? fdGetFILE(ifd) : stdin);
03432     char *q = buf - 1;          /* initialize just before buffer. */
03433     size_t nb = 0;
03434     size_t nr = 0;
03435     int pc = 0, bc = 0;
03436     char *p = buf;
03437 
03438 #ifdef  NOISY   /* XXX obliterates CLI input */
03439 SQLDBG((stderr, "--> %s(%p[%u],%p) ifd %p fp %p fileno %d fdno %d\n", __FUNCTION__, buf, (unsigned)nbuf, sql, ifd, ifp, (ifp ? fileno(ifp) : -3), Fileno(ifd)));
03440 #endif  /* NOISY */
03441 assert(ifp != NULL);
03442 
03443     if (ifp != NULL)
03444     do {
03445         *(++q) = '\0';                  /* terminate and move forward. */
03446         if (fgets(q, (int)nbuf, ifp) == NULL)   /* read next line. */
03447             break;
03448         nb = strlen(q);
03449         nr += nb;                       /* trim trailing \r and \n */
03450         for (q += nb - 1; nb > 0 && iseol(*q); q--)
03451             nb--;
03452         for (; p <= q; p++) {
03453             switch (*p) {
03454                 case '\\':
03455                     switch (*(p+1)) {
03456                         case '\r': /*@switchbreak@*/ break;
03457                         case '\n': /*@switchbreak@*/ break;
03458                         case '\0': /*@switchbreak@*/ break;
03459                         default: p++; /*@switchbreak@*/ break;
03460                     }
03461                     /*@switchbreak@*/ break;
03462                 case '%':
03463                     switch (*(p+1)) {
03464                         case '{': p++, bc++; /*@switchbreak@*/ break;
03465                         case '(': p++, pc++; /*@switchbreak@*/ break;
03466                         case '%': p++; /*@switchbreak@*/ break;
03467                     }
03468                     /*@switchbreak@*/ break;
03469                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
03470                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
03471                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
03472                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
03473             }
03474         }
03475         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
03476             *(++q) = '\0';              /* trim trailing \r, \n */
03477             break;
03478         }
03479         q++; p++; nb++;                 /* copy newline too */
03480         nbuf -= nb;
03481         if (*q == '\r')                 /* XXX avoid \r madness */
03482             *q = '\n';
03483     } while (nbuf > 0);
03484 
03485 SQLDBG((stderr, "<-- %s(%p[%u],%p) nr %u\n", __FUNCTION__, buf, (unsigned)nbuf, sql, (unsigned)nr));
03486 
03487     return (nr > 0 ? buf : NULL);
03488 }
03489 
03500 static char *local_getline(rpmsql sql, /*@null@*/const char *zPrompt)
03501 {
03502     char * t;
03503 
03504 SQLDBG((stderr, "--> %s(%s) ofd %p\n", __FUNCTION__, zPrompt, sql->ofd));
03505 
03506     if (sql->ofd && zPrompt && *zPrompt) {
03507         size_t nb = strlen(zPrompt);
03508         size_t nw = Fwrite(zPrompt, 1, nb, sql->ofd);
03509 assert(nb == nw);
03510         (void) Fflush(sql->ofd);
03511     }
03512 
03513 assert(sql->ifd != NULL);
03514     t = rpmsqlFgets(sql->buf, sql->nbuf, sql);
03515 
03516 SQLDBG((stderr, "<-- %s(%s) ofd %p\n", __FUNCTION__, zPrompt, sql->ofd));
03517 
03518     return t;
03519 }
03520 
03528 static char *rpmsqlInputOneLine(rpmsql sql, const char *zPrior)
03529 {
03530     const char *zPrompt;
03531     char *zResult;
03532 
03533 SQLDBG((stderr, "--> %s(%s)\n", __FUNCTION__, zPrior));
03534 
03535 assert(sql->buf != NULL);
03536 assert(sql->ifd != NULL);
03537 
03538     if (!F_ISSET(sql, PROMPT)) {
03539         zResult = local_getline(sql, NULL);
03540     } else {
03541         zPrompt = (zPrior && zPrior[0]) ? sql->zContinue : sql->zPrompt;
03542         zResult = readline(sql, zPrompt);
03543         if (zResult) {
03544 #if defined(HAVE_READLINE) && HAVE_READLINE==1
03545             if (*zResult)
03546                 add_history(zResult);
03547             /* XXX readline returns malloc'd memory. copy & free. */
03548             if (zResult != sql->buf) {
03549                 strncpy(sql->buf, zResult, sql->nbuf);
03550                 zResult = _free(zResult);
03551                 zResult = sql->buf;
03552             }
03553 #endif
03554         }
03555     }
03556 
03557 SQLDBG((stderr, "<-- %s(%s)\n", __FUNCTION__, zPrior));
03558 
03559     return zResult;
03560 }
03561 
03562 #endif  /* defined(WITH_SQLITE) */
03563 
03564 /*==============================================================*/
03565 
03566 #if defined(WITH_SQLITE)
03567 
03570 static char *save_err_msg(sqlite3 * db)
03571 {
03572     const char * s = sqlite3_errmsg(db);
03573     int nb = strlen30(s) + 1;
03574     return memcpy(xmalloc(nb), s, nb);
03575 }
03576 
03587 static int _rpmsqlShellExec(rpmsql sql, const char *zSql,
03588                       int (*xCallback) (void *, int, char **, char **, int *),
03589                       char **pzErrMsg
03590     )
03591 {
03592     sqlite3 * db = (sqlite3 *) sql->I;
03593     sqlite3_stmt * pStmt = NULL;        /* Statement to execute. */
03594     int rc = SQLITE_OK;         /* Return Code */
03595     const char *zLeftover;      /* Tail of unprocessed SQL */
03596 
03597 SQLDBG((stderr, "--> %s(%p,%s,%p,%p)\n", __FUNCTION__, sql, zSql, xCallback, pzErrMsg));
03598     if (pzErrMsg)
03599         *pzErrMsg = NULL;
03600 
03601     while (zSql[0] && rc == SQLITE_OK) {
03602         rc = rpmsqlCmd(sql, "prepare_v2", db,
03603                 sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover));
03604         if (rc)
03605             goto bottom;
03606 
03607         /* this happens for a comment or white-space */
03608         if (pStmt == NULL)
03609             goto bottom;
03610 
03611         /* echo the sql statement if echo on */
03612         if (sql->ofd && F_ISSET(sql, ECHO)) {
03613             const char *zStmtSql = sqlite3_sql(pStmt);
03614             rpmsqlFprintf(sql, "%s\n", zStmtSql ? zStmtSql : zSql);
03615             (void) Fflush(sql->ofd);
03616         }
03617 
03618         /* perform the first step.  this will tell us if we
03619          ** have a result set or not and how wide it is.
03620          */
03621         rc = rpmsqlCmd(sql, "step", db,
03622                 sqlite3_step(pStmt));
03623         /* if we have a result set... */
03624         if (rc == SQLITE_ROW) {
03625             /* if we have a callback... */
03626             if (xCallback) {
03627                 /* allocate space for col name ptr, value ptr, and type */
03628                 int nCol = sqlite3_column_count(pStmt);
03629                 size_t nb = 3 * nCol * sizeof(const char *) + 1;
03630                 char ** azCols = xmalloc(nb);           /* Result names */
03631                 char ** azVals = &azCols[nCol];         /* Result values */
03632                 int * aiTypes = (int *) &azVals[nCol];  /* Result types */
03633                 int i;
03634 
03635                 /* save off ptrs to column names */
03636                 for (i = 0; i < nCol; i++)
03637                     azCols[i] = (char *) sqlite3_column_name(pStmt, i);
03638 
03639                 /* save off the prepared statment handle and reset row count */
03640                 sql->S = (void *) pStmt;
03641                 sql->cnt = 0;
03642                 do {
03643                     /* extract the data and data types */
03644                     for (i = 0; i < nCol; i++) {
03645                         azVals[i] = (char *) sqlite3_column_text(pStmt, i);
03646                         aiTypes[i] = sqlite3_column_type(pStmt, i);
03647                         if (!azVals[i] && (aiTypes[i] != SQLITE_NULL)) {
03648                             rc = SQLITE_NOMEM;
03649                             break;      /* from for */
03650                         }
03651                     }   /* end for */
03652 
03653                     /* if data and types extraction failed... */
03654                     if (rc != SQLITE_ROW)
03655                         break;
03656 
03657                     /* call the supplied callback with the result row data */
03658                     if (xCallback (sql, nCol, azVals, azCols, aiTypes)) {
03659                         rc = SQLITE_ABORT;
03660                         break;
03661                     }
03662                     rc = rpmsqlCmd(sql, "step", db,
03663                                 sqlite3_step(pStmt));
03664                 } while (rc == SQLITE_ROW);
03665                 azCols = _free(azCols);
03666                 sql->S = NULL;
03667             } else {
03668                 do {
03669                     rc = rpmsqlCmd(sql, "step", db,
03670                                 sqlite3_step(pStmt));
03671                 } while (rc == SQLITE_ROW);
03672             }
03673         }
03674 
03675         /* Finalize the statement just executed. If this fails, save a 
03676          ** copy of the error message. Otherwise, set zSql to point to the
03677          ** next statement to execute. */
03678         rc = rpmsqlCmd(sql, "finalize", db,
03679                 sqlite3_finalize(pStmt));
03680 
03681 bottom:
03682         /* On error, retrieve message and exit. */
03683         if (rc) {
03684             if (pzErrMsg)
03685                 *pzErrMsg = save_err_msg(db);
03686             break;
03687         }
03688 
03689         /* Move to next sql statement */
03690         zSql = zLeftover;
03691         while (xisspace(zSql[0]))
03692             zSql++;
03693     }                           /* end while */
03694 
03695     return rc;
03696 }
03697 #endif  /* defined(WITH_SQLITE) */
03698 
03699 /*==============================================================*/
03700 
03701 #if defined(WITH_SQLITE)
03702 
03710 static int dump_callback(void *_sql, int nArg, char **azArg, char **azCol)
03711 {
03712     rpmsql sql = (rpmsql) _sql;
03713     sqlite3 * db = (sqlite3 *) sql->I;
03714     int rc;
03715     const char *zTable;
03716     const char *zType;
03717     const char *zSql;
03718     const char *zPrepStmt = 0;
03719     int ec = 1;         /* assume failure */
03720 
03721 SQLDBG((stderr, "--> %s(%p,%d,%p,%p)\n", __FUNCTION__, _sql, nArg, azArg, azCol));
03722     azCol = azCol;
03723     if (nArg != 3)
03724         goto exit;
03725     zTable = azArg[0];
03726     zType = azArg[1];
03727     zSql = azArg[2];
03728 
03729     if (!strcmp(zTable, "sqlite_sequence")) {
03730         zPrepStmt = "DELETE FROM sqlite_sequence;\n";
03731     } else if (!strcmp(zTable, "sqlite_stat1")) {
03732         rpmsqlFprintf(sql, "ANALYZE sqlite_master;\n");
03733     } else if (!strncmp(zTable, "sqlite_", 7)) {
03734         ec = 0;         /* XXX success */
03735         goto exit;
03736     } else if (!strncmp(zSql, "CREATE VIRTUAL TABLE", 20)) {
03737         char *zIns;
03738         if (!F_ISSET(sql, WRITABLE)) {
03739             rpmsqlFprintf(sql, "PRAGMA writable_schema=ON;\n");
03740             sql->flags |= RPMSQL_FLAGS_WRITABLE;
03741         }
03742         zIns =
03743             sqlite3_mprintf
03744             ("INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
03745              "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql);
03746         rpmsqlFprintf(sql, "%s\n", zIns);
03747         sqlite3_free(zIns);
03748         ec = 0;         /* XXX success */
03749         goto exit;
03750     } else
03751         rpmsqlFprintf(sql, "%s;\n", zSql);
03752 
03753     if (!strcmp(zType, "table")) {
03754         sqlite3_stmt * pTableInfo = NULL;
03755         char *zSelect = 0;
03756         char *zTableInfo = 0;
03757         char *zTmp = 0;
03758         int nRow = 0;
03759 
03760         zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
03761         zTableInfo = appendText(zTableInfo, zTable, '"');
03762         zTableInfo = appendText(zTableInfo, ");", 0);
03763 
03764         rc = rpmsqlCmd(sql, "prepare", db,
03765                 sqlite3_prepare(db, zTableInfo, -1, &pTableInfo, 0));
03766         zTableInfo = _free(zTableInfo);
03767         if (rc != SQLITE_OK || !pTableInfo)
03768             goto exit;
03769 
03770         zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
03771         zTmp = appendText(zTmp, zTable, '"');
03772         if (zTmp)
03773             zSelect = appendText(zSelect, zTmp, '\'');
03774         zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
03775         rc = rpmsqlCmd(sql, "step", db,
03776                 sqlite3_step(pTableInfo));
03777         while (rc == SQLITE_ROW) {
03778             const char *zText =
03779                 (const char *) sqlite3_column_text(pTableInfo, 1);
03780             zSelect = appendText(zSelect, "quote(", 0);
03781             zSelect = appendText(zSelect, zText, '"');
03782             rc = rpmsqlCmd(sql, "step", db,
03783                         sqlite3_step(pTableInfo));
03784             if (rc == SQLITE_ROW)
03785                 zSelect = appendText(zSelect, ") || ',' || ", 0);
03786             else
03787                 zSelect = appendText(zSelect, ") ", 0);
03788             nRow++;
03789         }
03790         rc = rpmsqlCmd(sql, "finalize", db,
03791                 sqlite3_finalize(pTableInfo));
03792         if (rc != SQLITE_OK || nRow == 0) {
03793             zSelect = _free(zSelect);
03794             goto exit;
03795         }
03796 
03797         zSelect = appendText(zSelect, "|| ')' FROM  ", 0);
03798         zSelect = appendText(zSelect, zTable, '"');
03799 
03800         rc = run_table_dump_query(sql, db, zSelect, zPrepStmt);
03801         if (rc == SQLITE_CORRUPT) {
03802             zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
03803             rc = run_table_dump_query(sql, db, zSelect, NULL);
03804         }
03805         zSelect = _free(zSelect);
03806     }
03807     ec = 0;     /* XXX success */
03808 exit:
03809     return ec;
03810 }
03811 
03820 static int run_schema_dump_query(rpmsql sql,
03821                                  const char *zQuery, char **pzErrMsg)
03822 {
03823     sqlite3 * db = (sqlite3 *) sql->I;
03824     int rc;
03825 
03826 SQLDBG((stderr, "--> %s(%p,%s,%p)\n", __FUNCTION__, sql, zQuery, pzErrMsg));
03827     rc = rpmsqlCmd(sql, "exec", db,
03828                 sqlite3_exec(db, zQuery, dump_callback, sql, pzErrMsg));
03829     if (rc == SQLITE_CORRUPT) {
03830         char *zQ2;
03831         if (pzErrMsg)
03832             sqlite3_free(*pzErrMsg);
03833         zQ2 = rpmExpand(zQuery, " ORDER BY rowid DESC", NULL);
03834         rc = rpmsqlCmd(sql, "exec", db,
03835                 sqlite3_exec(db, zQ2, dump_callback, sql, pzErrMsg));
03836         zQ2 = _free(zQ2);
03837     }
03838     return rc;
03839 }
03840 
03841 /*
03842  * Text of a help message
03843  */
03844 /*@unchecked@*/
03845 static char zHelp[] =
03846     ".backup ?DB? FILE      Backup DB (default \"main\") to FILE\n"
03847     ".bail ON|OFF           Stop after hitting an error.  Default OFF\n"
03848     ".databases             List names and files of attached databases\n"
03849     ".dump ?TABLE? ...      Dump the database in an SQL text format\n"
03850     "                         If TABLE specified, only dump tables matching\n"
03851     "                         LIKE pattern TABLE.\n"
03852     ".echo ON|OFF           Turn command echo on or off\n"
03853     ".exit                  Exit this program\n"
03854     ".explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.\n"
03855     "                         With no args, it turns EXPLAIN on.\n"
03856     ".header(s) ON|OFF      Turn display of headers on or off\n"
03857     ".help                  Show this message\n"
03858     ".import FILE TABLE     Import data from FILE into TABLE\n"
03859     ".indices ?TABLE?       Show names of all indices\n"
03860     "                         If TABLE specified, only show indices for tables\n"
03861     "                         matching LIKE pattern TABLE.\n"
03862 #ifdef SQLITE_ENABLE_IOTRACE
03863     ".iotrace FILE          Enable I/O diagnostic logging to FILE\n"
03864 #endif
03865     ".load FILE ?ENTRY?     Load an extension library\n"
03866     ".log FILE|off          Turn logging on or off.  FILE can be stderr/stdout\n"
03867     ".mode MODE ?TABLE?     Set output mode where MODE is one of:\n"
03868     "                         csv      Comma-separated values\n"
03869     "                         column   Left-aligned columns.  (See .width)\n"
03870     "                         html     HTML <table> code\n"
03871     "                         insert   SQL insert statements for TABLE\n"
03872     "                         line     One value per line\n"
03873     "                         list     Values delimited by .separator string\n"
03874     "                         tabs     Tab-separated values\n"
03875     "                         tcl      TCL list elements\n"
03876     ".nullvalue STRING      Print STRING in place of NULL values\n"
03877     ".output FILENAME       Send output to FILENAME\n"
03878     ".output stdout         Send output to the screen\n"
03879     ".prompt MAIN CONTINUE  Replace the standard prompts\n"
03880     ".quit                  Exit this program\n"
03881     ".read FILENAME         Execute SQL in FILENAME\n"
03882     ".restore ?DB? FILE     Restore content of DB (default \"main\") from FILE\n"
03883     ".schema ?TABLE?        Show the CREATE statements\n"
03884     "                         If TABLE specified, only show tables matching\n"
03885     "                         LIKE pattern TABLE.\n"
03886     ".separator STRING      Change separator used by output mode and .import\n"
03887     ".show                  Show the current values for various settings\n"
03888     ".tables ?TABLE?        List names of tables\n"
03889     "                         If TABLE specified, only list tables matching\n"
03890     "                         LIKE pattern TABLE.\n"
03891     ".timeout MS            Try opening locked tables for MS milliseconds\n"
03892     ".width NUM1 NUM2 ...   Set column widths for \"column\" mode\n";
03893 
03894 static char zTimerHelp[] =
03895     ".timer ON|OFF          Turn the CPU timer measurement on or off\n";
03896 
03906 static void resolve_backslashes(char *z)
03907 {
03908     int i, j;
03909     char c;
03910     for (i = j = 0; (c = z[i]) != 0; i++, j++) {
03911         if (c == '\\') {
03912             c = z[++i];
03913             if (c == 'n') {
03914                 c = '\n';
03915             } else if (c == 't') {
03916                 c = '\t';
03917             } else if (c == 'r') {
03918                 c = '\r';
03919             } else if (c >= '0' && c <= '7') {
03920                 c -= '0';
03921                 if (z[i + 1] >= '0' && z[i + 1] <= '7') {
03922                     i++;
03923                     c = (c << 3) + z[i] - '0';
03924                     if (z[i + 1] >= '0' && z[i + 1] <= '7') {
03925                         i++;
03926                         c = (c << 3) + z[i] - '0';
03927                     }
03928                 }
03929             }
03930         }
03931         z[j] = c;
03932     }
03933     z[j] = 0;
03934 }
03935 
03939 static int booleanValue(const char * zArg)
03940 {
03941     int val = atoi(zArg);
03942     if (!strcasecmp(zArg, "on") || !strcasecmp(zArg, "yes"))
03943         val = 1;
03944 SQLDBG((stderr, "<-- %s(%s) val %d\n", __FUNCTION__, zArg, val));
03945     return val;
03946 }
03947 
03948 /*@unchecked@*/ /*@observer@*/
03949 static const char *modeDescr[] = {
03950     "line",
03951     "column",
03952     "list",
03953     "semi",
03954     "html",
03955     "insert",
03956     "tcl",
03957     "csv",
03958     "explain",
03959 };
03960 
03961 /* forward ref @*/
03962 static int rpmsqlInput(rpmsql sql);
03963 
03964 static int rpmsqlFOpen(const char * fn, FD_t *fdp)
03965         /*@modifies *fdp @*/
03966 {
03967     FD_t fd = *fdp;
03968     int rc = 0;
03969 
03970 SQLDBG((stderr, "--> %s(%s,%p) fd %p\n", __FUNCTION__, fn, fdp, fd));
03971 
03972     if (fd)
03973         (void) Fclose(fd);      /* XXX stdout/stderr were dup'd */
03974     fd = NULL;
03975     /* XXX permit numeric fdno's? */
03976     if (fn == NULL)
03977         fd = NULL;
03978     else if (!strcmp(fn, "stdout") || !strcmp(fn, "-"))
03979         fd = fdDup(STDOUT_FILENO);
03980     else if (!strcmp(fn, "stderr"))
03981         fd = fdDup(STDERR_FILENO);
03982     else if (!strcmp(fn, "off"))
03983         fd = NULL;
03984     else {
03985         fd = Fopen(fn, "wb");
03986         if (fd == NULL || Ferror(fd)) {
03987             rpmsql_error(1, _("cannot open \"%s\""), fn);
03988             if (fd) (void) Fclose(fd);
03989             fd = NULL;
03990             rc = 1;
03991         }
03992     }
03993     *fdp = fd;
03994 
03995 SQLDBG((stderr, "<-- %s(%s,%p) fd %p rc %d\n", __FUNCTION__, fn, fdp, fd, rc));
03996 
03997     return rc;
03998 }
03999 
04006 static int rpmsqlMetaCommand(rpmsql sql, char *zLine)
04007 {
04008     sqlite3 * db = (sqlite3 *)sql->I;
04009     int i = 1;
04010     int nArg = 0;
04011     int n, c;
04012     int rc = 0;
04013     char *azArg[50];
04014 
04015 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, zLine));
04016 
04017     /* Parse the input line into tokens.  */
04018     while (zLine[i] && nArg < ArraySize(azArg)) {
04019         while (xisspace((unsigned char) zLine[i]))
04020             i++;
04021         if (zLine[i] == '\0')
04022             break;
04023         if (zLine[i] == '\'' || zLine[i] == '"') {
04024             int delim = zLine[i++];
04025             azArg[nArg++] = &zLine[i];
04026             while (zLine[i] && zLine[i] != delim)
04027                 i++;
04028             if (zLine[i] == delim)
04029                 zLine[i++] = '\0';
04030             if (delim == '"')
04031                 resolve_backslashes(azArg[nArg - 1]);
04032         } else {
04033             azArg[nArg++] = &zLine[i];
04034             while (zLine[i] && !xisspace((unsigned char) zLine[i]))
04035                 i++;
04036             if (zLine[i])
04037                 zLine[i++] = 0;
04038             resolve_backslashes(azArg[nArg - 1]);
04039         }
04040     }
04041 
04042     /* Process the input line. */
04043     if (nArg == 0)
04044         return 0;               /* no tokens, no error */
04045     n = strlen30(azArg[0]);
04046     c = azArg[0][0];
04047     if (c == 'b' && n >= 3 && !strncmp(azArg[0], "backup", n)
04048         && nArg > 1 && nArg < 4) {
04049         const char *zDestFile;
04050         const char *zDb;
04051         sqlite3 * pDest;
04052         sqlite3_backup *pBackup;
04053         if (nArg == 2) {
04054             zDestFile = azArg[1];
04055             zDb = "main";
04056         } else {
04057             zDestFile = azArg[2];
04058             zDb = azArg[1];
04059         }
04060         rc = rpmsqlCmd(sql, "open", pDest,
04061                 sqlite3_open(zDestFile, &pDest));
04062         if (rc) {
04063 #ifdef  DYING
04064             rpmsql_error(1, _("cannot open \"%s\""), zDestFile);
04065 #endif
04066             (void) rpmsqlCmd(sql, "close", pDest,
04067                 sqlite3_close(pDest));
04068             return 1;
04069         }
04070         _rpmsqlOpenDB(sql);
04071         db = (sqlite3 *)sql->I;
04072         pBackup = sqlite3_backup_init(pDest, "main", db, zDb);
04073         if (pBackup == NULL) {
04074             rpmsql_error(1, "%s", sqlite3_errmsg(pDest));
04075             (void) rpmsqlCmd(sql, "close", pDest,
04076                         sqlite3_close(pDest));
04077             return 1;
04078         }
04079         while ((rc = rpmsqlCmd(sql, "backup_step", db,
04080                         sqlite3_backup_step(pBackup, 100))) == SQLITE_OK)
04081             ;
04082         (void) rpmsqlCmd(sql, "backup_finish", pBackup,
04083                         sqlite3_backup_finish(pBackup));
04084         if (rc == SQLITE_DONE) {
04085             rc = 0;
04086         } else {
04087             rpmsql_error(1, "%s", sqlite3_errmsg(pDest));
04088             rc = 1;
04089         }
04090         (void) rpmsqlCmd(sql, "close", pDest,
04091                         sqlite3_close(pDest));
04092     } else
04093      if (c == 'b' && n >= 3 && !strncmp(azArg[0], "bail", n)
04094              && nArg > 1 && nArg < 3) {
04095         if (booleanValue(azArg[1]))
04096             sql->flags |= RPMSQL_FLAGS_BAIL;
04097         else
04098             sql->flags &= ~RPMSQL_FLAGS_BAIL;
04099     } else
04100      if (c == 'd' && n > 1 && !strncmp(azArg[0], "databases", n) && nArg == 1) {
04101         /* XXX recursion b0rkage lies here. */
04102         uint32_t _flags = sql->flags;
04103         uint32_t _mode = sql->mode;
04104         int _cnt = sql->cnt;;
04105         int _colWidth[3];
04106         char *zErrMsg = NULL;
04107         memcpy(_colWidth, sql->colWidth, sizeof(_colWidth));
04108         _rpmsqlOpenDB(sql);
04109         db = (sqlite3 *)sql->I;
04110         sql->flags |= RPMSQL_FLAGS_SHOWHDR;
04111         sql->mode = RPMSQL_MODE_COLUMN;
04112         sql->colWidth[0] = 3;
04113         sql->colWidth[1] = 15;
04114         sql->colWidth[2] = 58;
04115         sql->cnt = 0;
04116         (void) rpmsqlCmd(sql, "exec", db,
04117                 sqlite3_exec(db, "PRAGMA database_list;", callback, sql, &zErrMsg));
04118         if (zErrMsg) {
04119             rpmsql_error(1, "%s", zErrMsg);
04120             sqlite3_free(zErrMsg);
04121             rc = 1;
04122         }
04123         memcpy(sql->colWidth, _colWidth, sizeof(_colWidth));
04124         sql->cnt = _cnt;
04125         sql->mode = _mode;
04126         sql->flags = _flags;
04127     } else
04128      if (c == 'd' && !strncmp(azArg[0], "dump", n) && nArg < 3) {
04129         char * t;
04130         _rpmsqlOpenDB(sql);
04131         db = (sqlite3 *)sql->I;
04132         /* When playing back a "dump", the content might appear in an order
04133          ** which causes immediate foreign key constraints to be violated.
04134          ** So disable foreign-key constraint enforcement to prevent problems. */
04135         rpmsqlFprintf(sql, "PRAGMA foreign_keys=OFF;\n");
04136         rpmsqlFprintf(sql, "BEGIN TRANSACTION;\n");
04137         sql->flags &= ~RPMSQL_FLAGS_WRITABLE;
04138         (void) rpmsqlCmd(sql, "exec", db,
04139                 sqlite3_exec(db, "PRAGMA writable_schema=ON", 0, 0, 0));
04140         if (nArg == 1) {
04141             t = rpmExpand("SELECT name, type, sql FROM sqlite_master"
04142                           " WHERE sql NOT NULL AND type=='table'"
04143                           " AND name!='sqlite_sequence'", NULL);
04144             run_schema_dump_query(sql, t, NULL);
04145             t = _free(t);
04146             t = rpmExpand("SELECT name, type, sql FROM sqlite_master"
04147                           " WHERE name=='sqlite_sequence'", NULL);
04148             run_schema_dump_query(sql, t, NULL);
04149             t = _free(t);
04150             t = rpmExpand("SELECT sql FROM sqlite_master"
04151                           " WHERE sql NOT NULL AND type IN ('index','trigger','view')", NULL);
04152             run_table_dump_query(sql, db, t, NULL);
04153             t = _free(t);
04154         } else {
04155             int i;
04156             for (i = 1; i < nArg; i++) {
04157                 t = rpmExpand(  "SELECT name, type, sql FROM sqlite_master"
04158                                 " WHERE tbl_name LIKE '", azArg[i], "'"
04159                                 " AND type=='table' AND sql NOT NULL", NULL);
04160                 run_schema_dump_query(sql, t, NULL);
04161                 t = _free(t);
04162                 t = rpmExpand(  "SELECT sql FROM sqlite_master"
04163                                 " WHERE sql NOT NULL"
04164                                 " AND type IN ('index','trigger','view')"
04165                                 " AND tbl_name LIKE '", azArg[i], "'", NULL);
04166                 run_table_dump_query(sql, db, t, NULL);
04167                 t = _free(t);
04168             }
04169         }
04170         if (F_ISSET(sql, WRITABLE)) {
04171             rpmsqlFprintf(sql, "PRAGMA writable_schema=OFF;\n");
04172             sql->flags &= ~RPMSQL_FLAGS_WRITABLE;
04173         }
04174         (void) rpmsqlCmd(sql, "exec", db,
04175                 sqlite3_exec(db, "PRAGMA writable_schema=OFF", 0, 0, 0));
04176         rpmsqlFprintf(sql, "COMMIT;\n");
04177     } else
04178      if (c == 'e' && !strncmp(azArg[0], "echo", n) && nArg > 1 && nArg < 3) {
04179         if (booleanValue(azArg[1]))
04180             sql->flags |= RPMSQL_FLAGS_ECHO;
04181         else
04182             sql->flags &= ~RPMSQL_FLAGS_ECHO;
04183     } else
04184      if (c == 'e' && !strncmp(azArg[0], "exit", n) && nArg == 1) {
04185         rc = 2;
04186     } else
04187      if (c == 'e' && !strncmp(azArg[0], "explain", n) && nArg < 3) {
04188         int val = nArg >= 2 ? booleanValue(azArg[1]) : 1;
04189         if (val == 1) {
04190             if (!sql->explainPrev.valid) {
04191                 sql->explainPrev.valid = 1;
04192                 sql->explainPrev.mode = sql->mode;
04193                 sql->explainPrev.flags = sql->flags;
04194                 memcpy(sql->explainPrev.colWidth, sql->colWidth,
04195                        sizeof(sql->colWidth));
04196             }
04197             /* We could put this code under the !p->explainValid
04198              ** condition so that it does not execute if we are already in
04199              ** explain mode. However, always executing it allows us an easy
04200              ** way to reset to explain mode in case the user previously
04201              ** did an .explain followed by a .width, .mode or .header
04202              ** command.
04203              */
04204             sql->mode = RPMSQL_MODE_EXPLAIN;
04205             sql->flags |= RPMSQL_FLAGS_SHOWHDR;
04206             memset(sql->colWidth, 0, ArraySize(sql->colWidth));
04207             sql->colWidth[0] = 4;       /* addr */
04208             sql->colWidth[1] = 13;      /* opcode */
04209             sql->colWidth[2] = 4;       /* P1 */
04210             sql->colWidth[3] = 4;       /* P2 */
04211             sql->colWidth[4] = 4;       /* P3 */
04212             sql->colWidth[5] = 13;      /* P4 */
04213             sql->colWidth[6] = 2;       /* P5 */
04214             sql->colWidth[7] = 13;      /* Comment */
04215         } else if (sql->explainPrev.valid) {
04216             sql->explainPrev.valid = 0;
04217             sql->mode = sql->explainPrev.mode;
04218             sql->flags = sql->explainPrev.flags;
04219             memcpy(sql->colWidth, sql->explainPrev.colWidth,
04220                    sizeof(sql->colWidth));
04221         }
04222     } else
04223      if (c == 'h'
04224       && (!strncmp(azArg[0], "header", n) || !strncmp(azArg[0], "headers", n))
04225              && nArg > 1 && nArg < 3)
04226      {
04227         if (booleanValue(azArg[1]))
04228             sql->flags |= RPMSQL_FLAGS_SHOWHDR;
04229         else
04230             sql->flags &= ~RPMSQL_FLAGS_SHOWHDR;
04231     } else
04232      if (c == 'h' && !strncmp(azArg[0], "help", n)) {
04233         rpmsql_error(0, "%s", zHelp);
04234         if (HAS_TIMER)
04235             rpmsql_error(0, "%s", zTimerHelp);
04236     } else
04237      if (c == 'i' && !strncmp(azArg[0], "import", n) && nArg == 3) {
04238         char *zTable = azArg[2];        /* Insert data into this table */
04239         char *zFile = azArg[1]; /* The file from which to extract data */
04240         sqlite3_stmt * pStmt = NULL;/* A statement */
04241         int nCol;               /* Number of columns in the table */
04242         int nByte;              /* Number of bytes in an SQL string */
04243         int i, j;               /* Loop counters */
04244         int nSep;               /* Number of bytes in sql->separator[] */
04245         char *zSql;             /* An SQL statement */
04246         char *zLine;            /* A single line of input from the file */
04247         char **azCol;           /* zLine[] broken up into columns */
04248         char *zCommit;          /* How to commit changes */
04249         int lineno = 0;         /* Line number of input file */
04250 
04251         _rpmsqlOpenDB(sql);
04252         db = (sqlite3 *)sql->I;
04253         nSep = strlen30(sql->separator);
04254         if (nSep == 0) {
04255             rpmsql_error(1, _("non-null separator required for import"));
04256             return 1;
04257         }
04258         zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
04259 assert(zSql != NULL);
04260         nByte = strlen30(zSql);
04261         rc = rpmsqlCmd(sql, "prepare", db,
04262                 sqlite3_prepare(db, zSql, -1, &pStmt, 0));
04263         sqlite3_free(zSql);
04264         if (rc) {
04265 #ifdef  DYING
04266             sqlite3 * db = (sqlite3 *)sql->I;
04267             rpmsql_error(1, "%s", sqlite3_errmsg(db));
04268 #endif
04269             if (pStmt)
04270                 (void) rpmsqlCmd(sql, "finalize", db,
04271                         sqlite3_finalize(pStmt));
04272             return 1;
04273         }
04274         nCol = sqlite3_column_count(pStmt);
04275         (void) rpmsqlCmd(sql, "finalize", db,
04276                         sqlite3_finalize(pStmt));
04277         pStmt = 0;
04278         if (nCol == 0)
04279             return 0;           /* no columns, no error */
04280         zSql = xmalloc(nByte + 20 + nCol * 2);
04281         sqlite3_snprintf(nByte + 20, zSql, "INSERT INTO '%q' VALUES(?",
04282                          zTable);
04283         j = strlen30(zSql);
04284         for (i = 1; i < nCol; i++) {
04285             zSql[j++] = ',';
04286             zSql[j++] = '?';
04287         }
04288         zSql[j++] = ')';
04289         zSql[j] = 0;
04290         rc = rpmsqlCmd(sql, "prepare", db,
04291                 sqlite3_prepare(db, zSql, -1, &pStmt, 0));
04292         zSql = _free(zSql);
04293         if (rc) {
04294 #ifdef  DYING
04295             sqlite3 * db = (sqlite3 *)sql->I;
04296             rpmsql_error(1, "%s", sqlite3_errmsg(db));
04297 #endif
04298             if (pStmt)
04299                 (void) rpmsqlCmd(sql, "finalize", db,
04300                         sqlite3_finalize(pStmt));
04301             return 1;
04302         }
04303 assert(sql->ifd == NULL);
04304         sql->ifd = Fopen(zFile, "rb.fpio");
04305         if (sql->ifd == NULL || Ferror(sql->ifd)) {
04306             rpmsql_error(1, _("cannot open \"%s\""), zFile);
04307             (void) rpmsqlCmd(sql, "finalize", db,
04308                         sqlite3_finalize(pStmt));
04309             if (sql->ifd) (void) Fclose(sql->ifd);
04310             sql->ifd = NULL;
04311             return 1;
04312         }
04313 assert(sql->buf == NULL);
04314 sql->nbuf = BUFSIZ;
04315 sql->buf = xmalloc(sql->nbuf);
04316         azCol = malloc(sizeof(azCol[0]) * (nCol + 1));
04317         if (azCol == NULL) {
04318             if (sql->ifd) (void) Fclose(sql->ifd);
04319             sql->ifd = NULL;
04320             (void) rpmsqlCmd(sql, "finalize", db,
04321                         sqlite3_finalize(pStmt));
04322 assert(azCol);
04323         }
04324         (void) rpmsqlCmd(sql, "exec", db,
04325                 sqlite3_exec(db, "BEGIN", 0, 0, 0));
04326         zCommit = "COMMIT";
04327         while ((zLine = local_getline(sql, NULL)) != NULL) {
04328             char *z;
04329             i = 0;
04330             lineno++;
04331             azCol[0] = zLine;
04332             for (i = 0, z = zLine; *z && *z != '\n' && *z != '\r'; z++) {
04333                 if (*z == sql->separator[0] && !strncmp(z, sql->separator, nSep)) {
04334                     *z = '\0';
04335                     i++;
04336                     if (i < nCol) {
04337                         azCol[i] = &z[nSep];
04338                         z += nSep - 1;
04339                     }
04340                 }
04341             }                   /* end for */
04342             *z = '\0';
04343             if (i + 1 != nCol) {
04344                 rpmsql_error(1,
04345                         _("%s line %d: expected %d columns of data but found %d"),
04346                         zFile, lineno, nCol, i + 1);
04347                 zCommit = "ROLLBACK";
04348                 rc = 1;
04349                 break;          /* from while */
04350             }
04351             for (i = 0; i < nCol; i++)
04352                 rc = rpmsqlCmd(sql, "bind_text", db,
04353                         sqlite3_bind_text(pStmt, i + 1, azCol[i], -1, SQLITE_STATIC));
04354             rc = rpmsqlCmd(sql, "step", db,
04355                         sqlite3_step(pStmt));
04356             rc = rpmsqlCmd(sql, "reset", db,
04357                 sqlite3_reset(pStmt));
04358             if (rc) {
04359 #ifdef  DYING
04360                 sqlite3 * db = (sqlite3 *)sql->I;
04361                 rpmsql_error(1, "%s", sqlite3_errmsg(db));
04362 #endif
04363                 zCommit = "ROLLBACK";
04364                 rc = 1;
04365                 break;          /* from while */
04366             }
04367         }                       /* end while */
04368         azCol = _free(azCol);
04369         if (sql->ifd) (void) Fclose(sql->ifd);
04370         sql->ifd = NULL;
04371 sql->buf = _free(sql->buf);
04372 sql->nbuf = 0;
04373         (void) rpmsqlCmd(sql, "finalize", db,
04374                 sqlite3_finalize(pStmt));
04375         (void) rpmsqlCmd(sql, "exec", db,
04376                 sqlite3_exec(db, zCommit, 0, 0, 0));
04377     } else
04378      if (c == 'i' && !strncmp(azArg[0], "indices", n) && nArg < 3) {
04379         /* XXX recursion b0rkage lies here. */
04380         uint32_t _flags = sql->flags;
04381         uint32_t _mode = sql->mode;
04382         char * t;
04383         char *zErrMsg = NULL;
04384         _rpmsqlOpenDB(sql);
04385         db = (sqlite3 *)sql->I;
04386         sql->flags &= ~RPMSQL_FLAGS_SHOWHDR;
04387         sql->mode = RPMSQL_MODE_LIST;
04388         if (nArg == 1) {
04389             t = rpmExpand("SELECT name FROM sqlite_master"
04390                           " WHERE type='index' AND name NOT LIKE 'sqlite_%'"
04391                           " UNION ALL "
04392                           "SELECT name FROM sqlite_temp_master"
04393                           " WHERE type='index'"
04394                           " ORDER BY 1", NULL);
04395             rc = rpmsqlCmd(sql, "exec", db,
04396                         sqlite3_exec(db, t, callback, sql, &zErrMsg));
04397             t = _free(t);
04398         } else {
04399             t = rpmExpand("SELECT name FROM sqlite_master"
04400                           " WHERE type='index' AND tbl_name LIKE '", azArg[1], "'",
04401                           " UNION ALL "
04402                           "SELECT name FROM sqlite_temp_master"
04403                           " WHERE type='index' AND tbl_name LIKE '", azArg[1], "'",
04404                           " ORDER BY 1", NULL);
04405             rc = rpmsqlCmd(sql, "exec", db,
04406                         sqlite3_exec(db, t, callback, sql, &zErrMsg));
04407             t = _free(t);
04408         }
04409         if (zErrMsg) {
04410             rpmsql_error(1, "%s", zErrMsg);
04411             sqlite3_free(zErrMsg);
04412             rc = 1;
04413         } else if (rc) {
04414 #ifdef  DYING
04415             rpmsql_error(1, _("querying sqlite_master and sqlite_temp_master"));
04416 #endif
04417             rc = 1;
04418         }
04419         sql->mode = _mode;
04420         sql->flags = _flags;
04421     } else
04422 
04423 #ifdef SQLITE_ENABLE_IOTRACE
04424     if (c == 'i' && !strncmp(azArg[0], "iotrace", n)) {
04425         extern void (*sqlite3IoTrace) (const char *, ...);
04426         rc = rpmsqlFOpen((nArg >= 2 ? azArg[1] : NULL), &sql->tfd);
04427         sqlite3IoTrace = (sql->tfd ? iotracePrintf : NULL);
04428     } else
04429 #endif
04430 
04431     if (c == 'l' && !strncmp(azArg[0], "load", n) && nArg >= 2) {
04432         const char *zFile, *zProc;
04433         char *zErrMsg = 0;
04434         zFile = azArg[1];
04435         zProc = nArg >= 3 ? azArg[2] : 0;
04436         if (!F_ISSET(sql, NOLOAD)) {
04437             _rpmsqlOpenDB(sql);
04438             db = (sqlite3 *)sql->I;
04439             rc = rpmsqlCmd(sql, "load_extension", db,
04440                         sqlite3_load_extension(db, zFile, zProc, &zErrMsg));
04441             if (rc) {
04442                 rpmsql_error(1, "%s", zErrMsg);
04443                 sqlite3_free(zErrMsg);
04444                 rc = 1;
04445             }
04446         }
04447     } else
04448 
04449     if (c == 'l' && !strncmp(azArg[0], "log", n) && nArg >= 1) {
04450         /* XXX set rc? */
04451         (void) rpmsqlFOpen((nArg >= 2 ? azArg[1] : NULL), &sql->lfd);
04452     } else
04453      if (c == 'm' && !strncmp(azArg[0], "mode", n) && nArg == 2) {
04454         int n2 = strlen30(azArg[1]);
04455         if ((n2 == 4 && !strncmp(azArg[1], "line", n2))
04456             || (n2 == 5 && !strncmp(azArg[1], "lines", n2))) {
04457             sql->mode = RPMSQL_MODE_LINE;
04458         } else if ((n2 == 6 && !strncmp(azArg[1], "column", n2))
04459                    || (n2 == 7 && !strncmp(azArg[1], "columns", n2))) {
04460             sql->mode = RPMSQL_MODE_COLUMN;
04461         } else if (n2 == 4 && !strncmp(azArg[1], "list", n2)) {
04462             sql->mode = RPMSQL_MODE_LIST;
04463         } else if (n2 == 4 && !strncmp(azArg[1], "html", n2)) {
04464             sql->mode = RPMSQL_MODE_HTML;
04465         } else if (n2 == 3 && !strncmp(azArg[1], "tcl", n2)) {
04466             sql->mode = RPMSQL_MODE_TCL;
04467         } else if (n2 == 3 && !strncmp(azArg[1], "csv", n2)) {
04468             sql->mode = RPMSQL_MODE_CSV;
04469             (void) stpcpy(sql->separator, ",");
04470         } else if (n2 == 4 && !strncmp(azArg[1], "tabs", n2)) {
04471             sql->mode = RPMSQL_MODE_LIST;
04472             (void) stpcpy(sql->separator, "\t");
04473         } else if (n2 == 6 && !strncmp(azArg[1], "insert", n2)) {
04474             sql->mode = RPMSQL_MODE_INSERT;
04475             set_table_name(sql, "table");
04476         } else {
04477             rpmsql_error(1, _("mode should be one of: %s"),
04478                     "column csv html insert line list tabs tcl");
04479             rc = 1;
04480         }
04481     } else
04482      if (c == 'm' && !strncmp(azArg[0], "mode", n) && nArg == 3) {
04483         int n2 = strlen30(azArg[1]);
04484         if (n2 == 6 && !strncmp(azArg[1], "insert", n2)) {
04485             sql->mode = RPMSQL_MODE_INSERT;
04486             set_table_name(sql, azArg[2]);
04487         } else {
04488             rpmsql_error(1, _("invalid arguments: "
04489                     " \"%s\". Enter \".help\" for help"), azArg[2]);
04490             rc = 1;
04491         }
04492     } else
04493      if (c == 'n' && !strncmp(azArg[0], "nullvalue", n) && nArg == 2) {
04494         (void) stpncpy(sql->nullvalue, azArg[1], sizeof(sql->nullvalue)-1);
04495     } else
04496      if (c == 'o' && !strncmp(azArg[0], "output", n) && nArg == 2) {
04497         rc = rpmsqlFOpen((nArg >= 2 ? azArg[1] : NULL), &sql->ofd);
04498 
04499         /* Make sure sql->ofd squirts _SOMEWHERE_. Save the name too. */
04500         sql->outfile = _free(sql->outfile);
04501         if (sql->ofd)
04502             sql->outfile = xstrdup(azArg[1]);
04503         else {
04504             sql->ofd = fdDup(STDOUT_FILENO);
04505             sql->outfile = xstrdup("stdout");
04506         }
04507     } else
04508      if (c == 'p' && !strncmp(azArg[0], "prompt", n)
04509              && (nArg == 2 || nArg == 3)) {
04510         if (nArg >= 2) {
04511             sql->zPrompt = _free(sql->zPrompt);
04512             sql->zPrompt = xstrdup(azArg[1]);
04513         }
04514         if (nArg >= 3) {
04515             sql->zContinue = _free(sql->zContinue);
04516             sql->zContinue = xstrdup(azArg[2]);
04517         }
04518     } else
04519      if (c == 'q' && !strncmp(azArg[0], "quit", n) && nArg == 1) {
04520         rc = 2;
04521     } else
04522      if (c == 'r' && n >= 3 && !strncmp(azArg[0], "read", n)
04523              && nArg == 2) {
04524         FD_t _ifd = sql->ifd;
04525         sql->ifd = Fopen(azArg[1], "rb.fpio");
04526         if (sql->ifd == NULL || Ferror(sql->ifd)) {
04527             rpmsql_error(1, _("cannot open \"%s\""), azArg[1]);
04528             rc = 1;
04529         } else {
04530             /* XXX .read assumes .echo off? */
04531             rc = rpmsqlInput(sql);
04532         }
04533         if (sql->ifd) (void) Fclose(sql->ifd);
04534         sql->ifd = _ifd;
04535     } else
04536      if (c == 'r' && n >= 3 && !strncmp(azArg[0], "restore", n)
04537              && nArg > 1 && nArg < 4) {
04538         const char *zSrcFile;
04539         const char *zDb;
04540         sqlite3 * pSrc;
04541         sqlite3_backup *pBackup;
04542         int nTimeout = 0;
04543 
04544         if (nArg == 2) {
04545             zSrcFile = azArg[1];
04546             zDb = "main";
04547         } else {
04548             zSrcFile = azArg[2];
04549             zDb = azArg[1];
04550         }
04551         rc = rpmsqlCmd(sql, "open", pSrc,       /* XXX watchout: arg order */
04552                 sqlite3_open(zSrcFile, &pSrc));
04553         if (rc) {
04554 #ifdef  DYING
04555             rpmsql_error(1, _("cannot open \"%s\""), zSrcFile);
04556 #endif
04557             (void) rpmsqlCmd(sql, "close", pSrc,
04558                 sqlite3_close(pSrc));
04559             return 1;
04560         }
04561         _rpmsqlOpenDB(sql);
04562         db = (sqlite3 *)sql->I;
04563         pBackup = sqlite3_backup_init(db, zDb, pSrc, "main");
04564         if (pBackup == 0) {
04565             rpmsql_error(1, "%s", sqlite3_errmsg(db));
04566             (void) rpmsqlCmd(sql, "close", db,
04567                         sqlite3_close(pSrc));
04568             return 1;
04569         }
04570         while ((rc = sqlite3_backup_step(pBackup, 100)) == SQLITE_OK
04571                || rc == SQLITE_BUSY)
04572         {
04573             if (rc == SQLITE_BUSY) {
04574                 if (nTimeout++ >= 3)
04575                     break;
04576                 sqlite3_sleep(100);
04577             }
04578         }
04579         sqlite3_backup_finish(pBackup);
04580         switch (rc) {
04581         case SQLITE_DONE:
04582             rc = 0;
04583             break;
04584         case SQLITE_BUSY:
04585         case SQLITE_LOCKED:
04586             rpmsql_error(1, _("source database is busy"));
04587             rc = 1;
04588             break;
04589         default:
04590             rpmsql_error(1, "%s", sqlite3_errmsg(db));
04591             rc = 1;
04592             break;
04593         }
04594         (void) rpmsqlCmd(sql, "close", pSrc,
04595                 sqlite3_close(pSrc));
04596     } else
04597      if (c == 's' && !strncmp(azArg[0], "schema", n) && nArg < 3) {
04598         /* XXX recursion b0rkage lies here. */
04599         uint32_t _flags = sql->flags;
04600         uint32_t _mode = sql->mode;
04601         char *zErrMsg = 0;
04602         _rpmsqlOpenDB(sql);
04603         db = (sqlite3 *)sql->I;
04604         sql->flags &= ~RPMSQL_FLAGS_SHOWHDR;
04605         sql->mode = RPMSQL_MODE_SEMI;
04606         if (nArg > 1) {
04607             int i;
04608             for (i = 0; azArg[1][i]; i++)
04609                 azArg[1][i] = (char) tolower(azArg[1][i]);
04610             if (!strcmp(azArg[1], "sqlite_master")) {
04611                 char *new_argv[2], *new_colv[2];
04612                 new_argv[0] = "CREATE TABLE sqlite_master (\n"
04613                     "  type text,\n"
04614                     "  name text,\n"
04615                     "  tbl_name text,\n"
04616                     "  rootpage integer,\n" "  sql text\n" ")";
04617                 new_argv[1] = 0;
04618                 new_colv[0] = "sql";
04619                 new_colv[1] = 0;
04620                 callback(sql, 1, new_argv, new_colv);
04621                 rc = SQLITE_OK;
04622             } else if (!strcmp(azArg[1], "sqlite_temp_master")) {
04623                 char *new_argv[2], *new_colv[2];
04624                 new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
04625                     "  type text,\n"
04626                     "  name text,\n"
04627                     "  tbl_name text,\n"
04628                     "  rootpage integer,\n" "  sql text\n" ")";
04629                 new_argv[1] = 0;
04630                 new_colv[0] = "sql";
04631                 new_colv[1] = 0;
04632                 callback(sql, 1, new_argv, new_colv);
04633                 rc = SQLITE_OK;
04634             } else {
04635                 char * t;
04636                 t = rpmExpand(  "SELECT sql FROM "
04637                                 "  (SELECT sql sql, type type, tbl_name tbl_name, name name"
04638                                 "     FROM sqlite_master UNION ALL"
04639                                 "   SELECT sql, type, tbl_name, name FROM sqlite_temp_master)"
04640                                 " WHERE tbl_name LIKE '", azArg[1], "'"
04641                                 " AND type!='meta' AND sql NOTNULL "
04642                                 "ORDER BY substr(type,2,1), name", NULL);
04643                 rc = rpmsqlCmd(sql, "exec", db,
04644                         sqlite3_exec(db, t, callback, sql, &zErrMsg));
04645                 t = _free(t);
04646             }
04647             sql->mode = _mode;
04648             sql->flags = _flags;
04649         } else {
04650             rc = rpmsqlCmd(sql, "exec", db,
04651                         sqlite3_exec(db,
04652                               "SELECT sql FROM "
04653                               "  (SELECT sql sql, type type, tbl_name tbl_name, name name"
04654                               "     FROM sqlite_master UNION ALL"
04655                               "   SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
04656                               "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
04657                               "ORDER BY substr(type,2,1), name",
04658                               callback, sql, &zErrMsg));
04659         }
04660         if (zErrMsg) {
04661             rpmsql_error(1, "%s", zErrMsg);
04662             sqlite3_free(zErrMsg);
04663             rc = 1;
04664         } else if (rc != SQLITE_OK) {
04665             rpmsql_error(1, _("querying schema information"));
04666             rc = 1;
04667         } else {
04668             rc = 0;
04669         }
04670     } else
04671      if (c == 's' && !strncmp(azArg[0], "separator", n) && nArg == 2) {
04672         (void) stpncpy(sql->separator, azArg[1], sizeof(sql->separator)-1);
04673     } else
04674      if (c == 's' && !strncmp(azArg[0], "show", n) && nArg == 1) {
04675         int i;
04676         rpmsqlFprintf(sql, "%9.9s: %s\n", "echo", F_ISSET(sql, ECHO) ? "on" : "off");
04677         rpmsqlFprintf(sql, "%9.9s: %s\n", "explain",
04678                 sql->explainPrev.valid ? "on" : "off");
04679         rpmsqlFprintf(sql, "%9.9s: %s\n", "headers",
04680                 F_ISSET(sql, SHOWHDR) ? "on" : "off");
04681         rpmsqlFprintf(sql, "%9.9s: %s\n", "mode", modeDescr[sql->mode]);
04682         rpmsqlFprintf(sql, "%9.9s: ", "nullvalue");
04683         output_c_string(sql, sql->nullvalue);
04684         rpmsqlFprintf(sql, "\n");
04685         rpmsqlFprintf(sql, "%9.9s: %s\n", "output",
04686                 (sql->outfile ? sql->outfile : "stdout"));
04687         rpmsqlFprintf(sql, "%9.9s: ", "separator");
04688         output_c_string(sql, sql->separator);
04689         rpmsqlFprintf(sql, "\n");
04690         rpmsqlFprintf(sql, "%9.9s: ", "width");
04691         for (i = 0;
04692              i < (int) ArraySize(sql->colWidth) && sql->colWidth[i] != 0;
04693              i++)
04694         {
04695             rpmsqlFprintf(sql, "%d ", sql->colWidth[i]);
04696         }
04697         rpmsqlFprintf(sql, "\n");
04698     } else
04699      if (c == 't' && n > 1 && !strncmp(azArg[0], "tables", n) && nArg < 3) {
04700         char **azResult;
04701         int nRow;
04702         char *zErrMsg;
04703         _rpmsqlOpenDB(sql);
04704         db = (sqlite3 *)sql->I;
04705         if (nArg == 1) {
04706             rc = rpmsqlCmd(sql, "get_table", db,
04707                         sqlite3_get_table(db,
04708                                    "SELECT name FROM sqlite_master "
04709                                    "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' "
04710                                    "UNION ALL "
04711                                    "SELECT name FROM sqlite_temp_master "
04712                                    "WHERE type IN ('table','view') "
04713                                    "ORDER BY 1",
04714                                    &azResult, &nRow, 0, &zErrMsg));
04715         } else {
04716             char * t;
04717             t = rpmExpand("SELECT name FROM sqlite_master "
04718                           " WHERE type IN ('table','view') AND name LIKE '", azArg[1], "'"
04719                           " UNION ALL "
04720                           "SELECT name FROM sqlite_temp_master"
04721                           " WHERE type IN ('table','view') AND name LIKE '", azArg[1], "'"
04722                                    "ORDER BY 1", NULL);
04723             rc = rpmsqlCmd(sql, "get_table", db,
04724                         sqlite3_get_table(db, t, &azResult, &nRow, 0,&zErrMsg));
04725             t = _free(t);
04726         }
04727         if (zErrMsg) {
04728             rpmsql_error(1, "%s", zErrMsg);
04729             sqlite3_free(zErrMsg);
04730             rc = 1;
04731         } else if (rc) {
04732             rpmsql_error(1, _("querying sqlite_master and sqlite_temp_master"));
04733             rc = 1;
04734         } else {
04735             int len, maxlen = 0;
04736             int i, j;
04737             int nPrintCol, nPrintRow;
04738             for (i = 1; i <= nRow; i++) {
04739                 if (azResult[i] == 0)
04740                     continue;
04741                 len = strlen30(azResult[i]);
04742                 if (len > maxlen)
04743                     maxlen = len;
04744             }
04745             nPrintCol = 80 / (maxlen + 2);
04746             if (nPrintCol < 1)
04747                 nPrintCol = 1;
04748             nPrintRow = (nRow + nPrintCol - 1) / nPrintCol;
04749             for (i = 0; i < nPrintRow; i++) {
04750                 for (j = i + 1; j <= nRow; j += nPrintRow) {
04751                     char *zSp = j <= nPrintRow ? "" : "  ";
04752                     rpmsqlFprintf(sql, "%s%-*s", zSp, maxlen,
04753                            azResult[j] ? azResult[j] : "");
04754                 }
04755                 rpmsqlFprintf(sql, "\n");
04756             }
04757         }
04758         sqlite3_free_table(azResult);
04759     } else
04760      if (c == 't' && n > 4 && !strncmp(azArg[0], "timeout", n) && nArg == 2) {
04761         _rpmsqlOpenDB(sql);
04762         db = (sqlite3 *)sql->I;
04763         (void) rpmsqlCmd(sql, "busy_timeout", db,
04764                 sqlite3_busy_timeout(db, atoi(azArg[1])));
04765     } else
04766      if (HAS_TIMER && c == 't' && n >= 5
04767              && !strncmp(azArg[0], "timer", n) && nArg == 2) {
04768         sql->enableTimer = booleanValue(azArg[1]);
04769     } else
04770      if (c == 'w' && !strncmp(azArg[0], "width", n) && nArg > 1) {
04771         int j;
04772 assert(nArg <= ArraySize(azArg));
04773         for (j = 1; j < nArg && j < ArraySize(sql->colWidth); j++)
04774             sql->colWidth[j - 1] = atoi(azArg[j]);
04775     } else
04776     {
04777         rpmsql_error(1, _("unknown command or invalid arguments: "
04778                 " \"%s\". Enter \".help\" for help"), azArg[0]);
04779         rc = 1;
04780     }
04781 
04782     return rc;
04783 }
04784 
04785 #endif  /* defined(WITH_SQLITE) */
04786 
04787 /*==============================================================*/
04788 
04789 #if defined(WITH_SQLITE)
04790 
04795 static int _contains_semicolon(const char *z, int N)
04796 {
04797     int rc = 0;
04798     int i;
04799     for (i = 0; i < N; i++) {
04800         if (z[i] != ';')
04801             continue;
04802         rc = 1;
04803         break;
04804     }
04805 SQLDBG((stderr, "<-- %s(%s) rc %d\n", __FUNCTION__, z, rc));
04806     return rc;
04807 }
04808 
04812 static int _all_whitespace(const char *z)
04813 {
04814     int rc = 1;
04815 
04816     for (; *z; z++) {
04817         if (xisspace(*(unsigned char *) z))
04818             continue;
04819         if (*z == '/' && z[1] == '*') {
04820             z += 2;
04821             while (*z && (*z != '*' || z[1] != '/'))
04822                 z++;
04823             if (*z == '\0') {
04824                 rc = 0;
04825                 break;
04826             }
04827             z++;
04828             continue;
04829         }
04830         if (*z == '-' && z[1] == '-') {
04831             z += 2;
04832             while (*z && *z != '\n')
04833                 z++;
04834             if (*z == '\0')
04835                 break;
04836             continue;
04837         }
04838         rc = 0;
04839         break;
04840     }
04841 
04842 SQLDBG((stderr, "<-- %s(%s) rc %d\n", __FUNCTION__, z, rc));
04843     return rc;
04844 }
04845 
04851 static int _is_command_terminator(const char *zLine)
04852 {
04853     int rc = 1;
04854 
04855     while (xisspace(*(unsigned char *) zLine))
04856         zLine++;
04857     if (zLine[0] == '/' && _all_whitespace(&zLine[1]))
04858         goto exit;              /* Oracle */
04859     if (xtolower(zLine[0]) == 'g' && xtolower(zLine[1]) == 'o'
04860      && _all_whitespace(&zLine[2]))
04861         goto exit;              /* SQL Server */
04862     rc = 0;
04863 exit:
04864 SQLDBG((stderr, "<-- %s(%s) rc %d\n", __FUNCTION__, zLine, rc));
04865     return rc;
04866 }
04867 
04872 static int _is_complete(char *zSql, int nSql)
04873 {
04874     int rc = 1;
04875     if (zSql == NULL)
04876         goto exit;
04877     zSql[nSql] = ';';
04878     zSql[nSql + 1] = '\0';
04879     rc = sqlite3_complete(zSql);
04880     zSql[nSql] = '\0';
04881 exit:
04882 SQLDBG((stderr, "<-- %s(%s) rc %d\n", __FUNCTION__, zSql, rc));
04883     return rc;
04884 }
04885 
04896 static int rpmsqlInput(rpmsql sql)
04897 {
04898     sqlite3 * db = (sqlite3 *) sql->I;
04899     char *zLine = 0;
04900     char *zSql = 0;
04901     int nSql = 0;
04902     int nSqlPrior = 0;
04903     char *zErrMsg;
04904     int rc;
04905     int errCnt = 0;
04906     int lineno = 0;
04907     int startline = 0;
04908 
04909 char * _buf = sql->buf;
04910 size_t _nbuf = sql->nbuf;
04911 
04912 SQLDBG((stderr, "--> %s(%p)\n", __FUNCTION__, sql));
04913 if (_rpmsql_debug < 0)
04914 rpmsqlDebugDump(sql);
04915 
04916 sql->nbuf = BUFSIZ;
04917 sql->buf = xmalloc(sql->nbuf);
04918 
04919     while (errCnt == 0 || !F_ISSET(sql, BAIL) || F_ISSET(sql, PROMPT))
04920     {
04921         if (sql->ofd) Fflush(sql->ofd);
04922         zLine = rpmsqlInputOneLine(sql, zSql);
04923         if (zLine == NULL)
04924             break;              /* We have reached EOF */
04925         if (_rpmsqlSeenInterrupt) {
04926             if (!F_ISSET(sql, PROMPT))
04927                 break;
04928             _rpmsqlSeenInterrupt = 0;
04929         }
04930         lineno++;
04931         if ((zSql == NULL || zSql[0] == '\0') && _all_whitespace(zLine))
04932             continue;
04933         if (zLine && zLine[0] == '.' && nSql == 0) {
04934             if (F_ISSET(sql, ECHO))
04935                 rpmsqlFprintf(sql, "%s\n", zLine);
04936             rc = rpmsqlMetaCommand(sql, zLine);
04937             if (rc == 2)        /* exit requested */
04938                 break;
04939             else if (rc)
04940                 errCnt++;
04941             continue;
04942         }
04943         if (_is_command_terminator(zLine) && _is_complete(zSql, nSql))
04944             memcpy(zLine, ";", 2);
04945         nSqlPrior = nSql;
04946         if (zSql == NULL) {
04947             int i;
04948             for (i = 0; zLine[i] && xisspace((unsigned char) zLine[i]); i++)
04949                 ;
04950             if (zLine[i] != '\0') {
04951                 nSql = strlen30(zLine);
04952                 zSql = xmalloc(nSql + 3);
04953                 memcpy(zSql, zLine, nSql + 1);
04954                 startline = lineno;
04955             }
04956         } else {
04957             int len = strlen30(zLine);
04958             zSql = xrealloc(zSql, nSql + len + 4);
04959             zSql[nSql++] = '\n';
04960             memcpy(&zSql[nSql], zLine, len + 1);
04961             nSql += len;
04962         }
04963         if (zSql && _contains_semicolon(&zSql[nSqlPrior], nSql - nSqlPrior)
04964             && sqlite3_complete(zSql)) {
04965             sql->cnt = 0;
04966             _rpmsqlOpenDB(sql);
04967             db = (sqlite3 *)sql->I;
04968             BEGIN_TIMER(sql);
04969             rc = _rpmsqlShellExec(sql, zSql, _rpmsqlShellCallback, &zErrMsg);
04970             END_TIMER(sql);
04971             if (rc || zErrMsg) {
04972                 char zPrefix[100];
04973                 if (!F_ISSET(sql, PROMPT) || !F_ISSET(sql, INTERACTIVE))
04974                     snprintf(zPrefix, sizeof(zPrefix),
04975                                      "near line %d: ", startline);
04976                 else
04977                     zPrefix[0] = '\0';
04978                 rpmsql_error(1, "%s%s", zPrefix,
04979                         zErrMsg ? zErrMsg : sqlite3_errmsg(db));
04980                 zErrMsg = _free(zErrMsg);
04981                 errCnt++;
04982             }
04983             zSql = _free(zSql);
04984             nSql = 0;
04985         }
04986     }
04987     if (zSql) {
04988         if (!_all_whitespace(zSql))
04989             rpmsql_error(1, _("incomplete SQL: %s"), zSql);
04990         zSql = _free(zSql);
04991     }
04992 
04993 sql->buf = _free(sql->buf);
04994 sql->buf = _buf;
04995 sql->nbuf = _nbuf;
04996 
04997 SQLDBG((stderr, "<-- %s(%p) rc %d\n", __FUNCTION__, sql, errCnt));
04998 
04999     return errCnt;
05000 }
05001 
05009 static int rpmsqlInitRC(rpmsql sql, const char *sqliterc)
05010 {
05011     int rc = 0;
05012 
05013 SQLDBG((stderr, "--> %s(%p,%s)\n", __FUNCTION__, sql, sqliterc));
05014 if (_rpmsql_debug < 0)
05015 rpmsqlDebugDump(sql);
05016 
05017     if (sqliterc == NULL) 
05018         sqliterc = sql->zInitrc;
05019     if (sqliterc) {
05020         FD_t _ifd = sql->ifd;
05021         sql->ifd = Fopen(sqliterc, "rb.fpio");
05022         if (!(sql->ifd == NULL || Ferror(sql->ifd))) {
05023             if (F_ISSET(sql, INTERACTIVE))
05024                 rpmsql_error(0, "-- Loading resources from %s", sqliterc);
05025             rc = rpmsqlInput(sql);
05026         }
05027         if (sql->ifd) (void) Fclose(sql->ifd);
05028         sql->ifd = _ifd;
05029     }
05030 
05031 SQLDBG((stderr, "<-- %s(%p,%s) rc %d\n", __FUNCTION__, sql, sqliterc, rc));
05032 
05033     return rc;
05034 }
05035 
05036 #endif  /* defined(WITH_SQLITE) */
05037 
05038 /*==============================================================*/
05039 
05040 #if defined(WITH_SQLITE)
05041 
05044 static void rpmsqlArgCallback(poptContext con,
05045                               /*@unused@ */ enum poptCallbackReason reason,
05046                               const struct poptOption *opt,
05047                               const char *arg,
05048                               /*@unused@ */ void *_data)
05049         /*@ */
05050 {
05051     rpmsql sql = &_sql;
05052 
05053     /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
05054     if (opt->arg == NULL)
05055     switch (opt->val) {
05056     case 'S':                           /*    -separator x */
05057 assert(arg != NULL);
05058         (void) stpncpy(sql->separator, arg, sizeof(sql->separator)-1);
05059         break;
05060     case 'N':                           /*    -nullvalue text */
05061 assert(arg != NULL);
05062         (void) stpncpy(sql->nullvalue, arg, sizeof(sql->nullvalue)-1);
05063         break;
05064     case 'V':                           /*    -version */
05065         printf("%s\n", sqlite3_libversion());
05066         /*@-exitarg@ */ exit(0); /*@=exitarg@ */
05067         /*@notreached@ */ break;
05068     default:
05069         /* XXX fprintf(stderr, ...)? */
05070         rpmsql_error(0, _("%s: Unknown callback(0x%x)\n"),
05071                     __FUNCTION__, (unsigned) opt->val);
05072         poptPrintUsage(con, stderr, 0);
05073         /*@-exitarg@ */ exit(2); /*@=exitarg@ */
05074         /*@notreached@ */ break;
05075     }
05076 }
05077 
05078 /*@unchecked@*/ /*@observer@*/
05079 static struct poptOption _rpmsqlOptions[] = {
05080     /*@-type@*//* FIX: cast? */
05081     {NULL, '\0',
05082      POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
05083      rpmsqlArgCallback, 0, NULL, NULL},
05084 /*@=type@*/
05085 
05086  { "debug", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH|POPT_ARGFLAG_DOC_HIDDEN, &_rpmsql_debug, -1,
05087         N_("Debug embedded SQL interpreter"), NULL},
05088 
05089  { "init", '\0', POPT_ARG_STRING|POPT_ARGFLAG_ONEDASH,  &_sql.zInitFile, 0,
05090         N_("read/process named FILE"), N_("FILE") },
05091  { "echo", '\0', POPT_BIT_SET|POPT_ARGFLAG_ONEDASH,     &_sql.flags, RPMSQL_FLAGS_ECHO,
05092         N_("print commands before execution"), NULL },
05093 
05094  { "load", '\0', POPT_BIT_SET|POPT_ARGFLAG_TOGGLE|POPT_ARGFLAG_ONEDASH, &_sql.flags, RPMSQL_FLAGS_NOLOAD,
05095         N_("disable extnsion loading (normally enabled)"), NULL },
05096  { "header", '\0', POPT_BIT_SET|POPT_ARGFLAG_TOGGLE|POPT_ARGFLAG_ONEDASH, &_sql.flags, RPMSQL_FLAGS_SHOWHDR,
05097         N_("turn headers on or off"), NULL },
05098 
05099  { "bail", '\0', POPT_BIT_SET|POPT_ARGFLAG_ONEDASH,     &_sql.flags, RPMSQL_FLAGS_BAIL,
05100         N_("stop after hitting an error"), NULL },
05101 
05102  { "interactive", '\0', POPT_BIT_SET|POPT_ARGFLAG_TOGGLE|POPT_ARGFLAG_ONEDASH,  &_sql.flags, RPMSQL_FLAGS_INTERACTIVE,
05103         N_("force interactive I/O"), NULL },
05104  { "batch", '\0', POPT_BIT_CLR|POPT_ARGFLAG_TOGGLE|POPT_ARGFLAG_ONEDASH, &_sql.flags, RPMSQL_FLAGS_INTERACTIVE,
05105         N_("force batch I/O"), NULL },
05106 
05107  { "column", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH,   &_sql.mode, RPMSQL_MODE_COLUMN,
05108         N_("set output mode to 'column'"), NULL },
05109  { "csv", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH,      &_sql.mode, RPMSQL_MODE_CSV,
05110         N_("set output mode to 'csv'"), NULL },
05111  { "html", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH,     &_sql.mode, RPMSQL_MODE_HTML,
05112         N_("set output mode to HTML"), NULL },
05113  { "line", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH,     &_sql.mode, RPMSQL_MODE_LINE,
05114         N_("set output mode to 'line'"), NULL },
05115  { "list", '\0', POPT_ARG_VAL|POPT_ARGFLAG_ONEDASH,     &_sql.mode, RPMSQL_MODE_LIST,
05116         N_("set output mode to 'list'"), NULL },
05117  { "separator", '\0', POPT_ARG_STRING|POPT_ARGFLAG_ONEDASH,     0, 'S',
05118         N_("set output field separator (|)"), N_("CHAR") },
05119  { "nullvalue", '\0', POPT_ARG_STRING|POPT_ARGFLAG_ONEDASH,     0, 'N',
05120         N_("set text string for NULL values"), N_("TEXT") },
05121 
05122  { "version", '\0', POPT_ARG_NONE|POPT_ARGFLAG_ONEDASH, 0, 'V',
05123         N_("show SQLite version"), NULL},
05124 
05125 #ifdef  NOTYET
05126  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
05127         N_("Common options for all rpmio executables:"), NULL},
05128 #endif
05129 
05130     POPT_AUTOHELP {NULL, (char) -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
05131         N_("\
05132 Usage: dbsql [OPTIONS] FILENAME [SQL]\n\
05133 FILENAME is the name of an SQLite database. A new database is created\n\
05134 if the file does not previously exist.\n\
05135 \n\
05136 OPTIONS include:\n\
05137    -help                show this message\n\
05138    -init filename       read/process named file\n\
05139    -echo                print commands before execution\n\
05140    -[no]header          turn headers on or off\n\
05141    -bail                stop after hitting an error\n\
05142    -interactive         force interactive I/O\n\
05143    -batch               force batch I/O\n\
05144    -column              set output mode to 'column'\n\
05145    -csv                 set output mode to 'csv'\n\
05146    -html                set output mode to HTML\n\
05147    -line                set output mode to 'line'\n\
05148    -list                set output mode to 'list'\n\
05149    -separator 'x'       set output field separator (|)\n\
05150    -nullvalue 'text'    set text string for NULL values\n\
05151    -version             show SQLite version\n\
05152 "), NULL},
05153 
05154     POPT_TABLEEND
05155 };
05156 
05157 #endif  /* defined(WITH_SQLITE) */
05158 
05159 /*==============================================================*/
05160 
05164 static void rpmsqlFini(void * _sql)
05165         /*@globals fileSystem @*/
05166         /*@modifies *_sql, fileSystem @*/
05167 {
05168     rpmsql sql = _sql;
05169 
05170 SQLDBG((stderr, "==> %s(%p)\n", __FUNCTION__, sql));
05171 
05172     sql->zDestTable = _free(sql->zDestTable);
05173 
05174     if (sql->ifd)
05175         (void) Fclose(sql->ifd);        /* XXX stdin dup'd */
05176     sql->ifd = NULL;
05177     if (sql->ofd)
05178         (void) Fclose(sql->ofd);        /* XXX stdout/stderr were dup'd */
05179     sql->ofd = NULL;
05180     if (sql->lfd)
05181         (void) Fclose(sql->lfd);
05182     sql->lfd = NULL;
05183     if (sql->tfd)
05184         (void) Fclose(sql->tfd);
05185     sql->tfd = NULL;
05186 
05187     sql->buf = _free(sql->buf);
05188     sql->buf = sql->b = NULL;
05189     sql->nbuf = sql->nb = 0;
05190 
05191     /* XXX INTERACTIVE cruft. */
05192     sql->zHome = _free(sql->zHome);
05193     sql->zInitrc = _free(sql->zInitrc);
05194     sql->zHistory = _free(sql->zHistory);
05195     sql->zPrompt = _free(sql->zPrompt);
05196     sql->zContinue = _free(sql->zContinue);
05197 
05198     sql->outfile = _free(sql->outfile);
05199 
05200     sql->zDbFilename = _free(sql->zDbFilename);
05201     sql->zInitFile = _free(sql->zInitFile);
05202     sql->av = argvFree(sql->av);
05203 #if defined(WITH_SQLITE)
05204     if (sql->I) {
05205         sqlite3 * db = (sqlite3 *)sql->I;
05206         (void) rpmsqlCmd(sql, "close", db,
05207                 sqlite3_close(db));
05208     }
05209 #endif
05210     sql->I = NULL;
05211     (void) rpmiobFree(sql->iob);
05212     sql->iob = NULL;
05213 }
05214 
05215 /*@unchecked@*/ /*@only@*/ /*@null@*/
05216 rpmioPool _rpmsqlPool;
05217 
05218 static rpmsql rpmsqlGetPool(/*@null@*/ rpmioPool pool)
05219         /*@globals _rpmsqlPool, fileSystem @*/
05220         /*@modifies pool, _rpmsqlPool, fileSystem @*/
05221 {
05222     rpmsql sql;
05223 
05224     if (_rpmsqlPool == NULL) {
05225         _rpmsqlPool = rpmioNewPool("sql", sizeof(*sql), -1, _rpmsql_debug,
05226                         NULL, NULL, rpmsqlFini);
05227         pool = _rpmsqlPool;
05228     }
05229     sql = (rpmsql) rpmioGetPool(pool, sizeof(*sql));
05230     memset(((char *)sql)+sizeof(sql->_item), 0, sizeof(*sql)-sizeof(sql->_item));
05231     return sql;
05232 }
05233 
05234 const char ** rpmsqlArgv(rpmsql sql, int * argcp)
05235 {
05236     const char ** av = sql->av;
05237 
05238     if (argcp)
05239         *argcp = argvCount(av);
05240     return av;
05241 }
05242 
05243 #if defined(WITH_SQLITE)
05244 
05248 static void rpmsqlInitPopt(rpmsql sql, int ac, char ** av, poptOption tbl)
05249         /*@modifies sql @*/
05250 {
05251     poptContext con;
05252     int rc;
05253 
05254     if (av == NULL || av[0] == NULL || av[1] == NULL)
05255         goto exit;
05256 
05257     con = poptGetContext(av[0], ac, (const char **)av, tbl, 0);
05258 
05259     /* Process all options into _sql, whine if unknown options. */
05260     while ((rc = poptGetNextOpt(con)) > 0) {
05261         const char * arg = poptGetOptArg(con);
05262         arg = _free(arg);
05263         switch (rc) {
05264         default:
05265             /* XXX fprintf(stderr, ...)? */
05266             rpmsql_error(0, _("%s: option table misconfigured (%d)\n"),
05267                         __FUNCTION__, rc);
05268             break;
05269         }
05270     }
05271     /* XXX FIXME: arrange error return iff rc < -1. */
05272 if (rc)
05273 SQLDBG((stderr, "%s: poptGetNextOpt rc(%d): %s\n", __FUNCTION__, rc, poptStrerror(rc)));
05274 
05275     /* Move the POPT parsed values into the current rpmsql object. */
05276     sql->flags = _sql.flags;
05277     sql->mode = _sql.mode;
05278     if (_sql.zInitFile) {
05279         sql->zInitFile = _free(sql->zInitFile);
05280         sql->zInitFile = _sql.zInitFile;
05281         _sql.zInitFile = NULL;
05282     }
05283     memcpy(sql->separator, _sql.separator, sizeof(sql->separator));
05284     memcpy(sql->nullvalue, _sql.nullvalue, sizeof(sql->nullvalue));
05285 
05286     sql->av = argvFree(sql->av);
05287     rc = argvAppend(&sql->av, poptGetArgs(con));
05288 
05289     con = poptFreeContext(con);
05290 
05291 exit:
05292     /* If not overridden, set the separator according to mode. */
05293     if (sql->separator[0] == '\0')
05294     switch (sql->mode) {
05295     default:
05296     case RPMSQL_MODE_LIST:      (void)stpcpy(sql->separator, "|");      break;
05297     case RPMSQL_MODE_CSV:       (void)stpcpy(sql->separator, ",");      break;
05298     }
05299 
05300 SQLDBG((stderr, "<== %s(%p, %p[%u], %p)\n", __FUNCTION__, sql, av, (unsigned)ac, tbl));
05301 }
05302 #endif /* defined(WITH_SQLITE) */
05303 
05304 rpmsql rpmsqlNew(char ** av, uint32_t flags)
05305 {
05306     rpmsql sql =
05307         (flags & 0x80000000) ? rpmsqlI() :
05308         rpmsqlGetPool(_rpmsqlPool);
05309     int ac = argvCount((ARGV_t)av);
05310 
05311 SQLDBG((stderr, "==> %s(%p[%u], 0x%x)\n", __FUNCTION__, av, (unsigned)ac, flags));
05312 if (av && _rpmsql_debug < 0)
05313 argvPrint("argv", (ARGV_t)av, NULL);
05314 
05315     sql->flags = flags;         /* XXX useful? */
05316 
05317 #if defined(WITH_SQLITE)
05318     /* XXX Avoid initialization on global interpreter creation path. */
05319     if (av) {
05320         static int _oneshot;
05321         sqlite3 * db = NULL;
05322         int xx;
05323 
05324         if (!_oneshot) {
05325 #if defined(SQLITE_CONFIG_LOG)
05326             sqlite3_config(SQLITE_CONFIG_LOG, shellLog, sql);
05327 #endif
05328             sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
05329             _oneshot++;
05330         }
05331 
05332         /* Initialize defaults for popt parsing. */
05333         memset(&_sql, 0, sizeof(_sql));
05334         sql->flags = _sql.flags = flags; /* XXX INTERACTIVE defaulted here. */
05335         sql->mode = _sql.mode = RPMSQL_MODE_LIST;
05336 
05337         rpmsqlInitPopt(sql, ac, av, (poptOption) _rpmsqlOptions);
05338 
05339         /* The 1st argument is the database to open (or :memory: default). */
05340         if (sql->av && sql->av[0]) {
05341             sql->zDbFilename = xstrdup(sql->av[0]);     /* XXX strdup? */
05342             /* If database alread exists, open immediately. */
05343             if (!Access(sql->zDbFilename, R_OK)) {
05344                 xx = rpmsqlCmd(sql, "open", db, /* XXX watchout: arg order */
05345                         sqlite3_open(sql->zDbFilename, &db));
05346                 sql->I = (void *) db;
05347             }
05348         } else
05349             sql->zDbFilename = xstrdup(":memory:");
05350 
05351         /* Read ~/.sqliterc (if specified), then reparse options. */
05352         if (sql->zInitFile || F_ISSET(sql, INTERACTIVE)) {
05353             sql->ofd = fdDup(STDOUT_FILENO);
05354             xx = rpmsqlInitRC(sql, sql->zInitFile);
05355             if (sql->ofd) (void) Fclose(sql->ofd);
05356             sql->ofd = NULL;
05357             rpmsqlInitPopt(sql, ac, av, (poptOption) _rpmsqlOptions);
05358         }
05359 
05360     }
05361 
05362     { /* XXX INTERACTIVE cruft. */
05363         static const char _zInitrc[]    = "/.sqliterc";
05364         static const char _zHistory[]   = "/.sqlite_history";
05365         /* XXX getpwuid? */
05366 sql->zHome = _free(sql->zHome);
05367         sql->zHome = xstrdup(getenv("HOME"));
05368 sql->zInitrc = _free(sql->zInitrc);
05369         sql->zInitrc = rpmGetPath(sql->zHome, _zInitrc, NULL);
05370 sql->zHistory = _free(sql->zHistory);
05371         sql->zHistory = rpmGetPath(sql->zHome, _zHistory, NULL);
05372         /*
05373          ** Prompt strings. Initialized in main. Settable with
05374          **   .prompt main continue
05375          */
05376         /* Initialize the prompt from basename(argv[0]). */
05377         if (sql->zPrompt == NULL) {     /* XXX this test is useless */
05378             char * t = xstrdup((av && av[0] ? av[0] : "sql"));
05379             char * bn = basename(t);
05380 sql->zPrompt = _free(sql->zPrompt);
05381             sql->zPrompt = rpmExpand(bn, "> ", NULL);
05382             t = _free(t);
05383 sql->zContinue = _free(sql->zContinue);
05384             sql->zContinue = t = xstrdup(sql->zPrompt);
05385             while (*t && *t != '>')
05386                 *t++ = '-';
05387         }
05388     }
05389 #else   /* WITH_SQLITE */
05390     if (av)
05391         (void) argvAppend(&sql->av, (ARGV_t) av);       /* XXX useful? */
05392 #endif  /* WITH_SQLITE */
05393 
05394     /* Set sane defaults for output sink(s) dependent on INTERACTIVE. */
05395     if (F_ISSET(sql, INTERACTIVE)) {
05396         if (sql->ofd == NULL)
05397             sql->ofd = fdDup(STDOUT_FILENO);
05398     } else {
05399         if (sql->iob == NULL)
05400             sql->iob = rpmiobNew(0);
05401     }
05402 
05403     return rpmsqlLink(sql);
05404 }
05405 
05406 rpmRC rpmsqlRun(rpmsql sql, const char * str, const char ** resultp)
05407 {
05408     rpmRC rc = RPMRC_FAIL;
05409 
05410 SQLDBG((stderr, "==> %s(%p,%p[%u]) \"%s\"\n", __FUNCTION__, sql, str, (unsigned)(str ? strlen(str) : 0), str));
05411 SQLDBG((stderr, "==========>\n%s\n<==========\n", str));
05412 
05413     if (sql == NULL) sql = rpmsqlI();
05414 
05415 #if defined(WITH_SQLITE)
05416     if (str != NULL) {
05417         const char * s = str;
05418 
05419         /* Ignore leading whitespace. */
05420         while (*s && xisspace((int)*s))
05421             s++;
05422 
05423         /* Perform the SQL operation(s). */
05424         if (*s == '\0') {                               /* INTERACTIVE */
05425             static int oneshot;
05426             uint32_t _flags = sql->flags;
05427             FD_t _ofd = sql->ofd;
05428             FD_t _ifd = sql->ifd;
05429 
05430 SQLDBG((stderr, "*** %s: INTERACTIVE\n", __FUNCTION__));
05431             sql->flags |= RPMSQL_FLAGS_INTERACTIVE;
05432             if (sql->ofd == NULL)
05433                 sql->ofd = fdDup(STDOUT_FILENO);
05434             if (!oneshot) {
05435 #ifdef  REFERENCE
05436                 extern char *db_full_version(int *, int *, int *, int *, int *);
05437                 fprintf(sql->out, "%s\n"
05438                         "Enter \".help\" for instructions\n"
05439                         "Enter SQL statements terminated with a \";\"\n",
05440                         db_full_version(NULL, NULL, NULL, NULL, NULL));
05441 #endif
05442                 size_t nb;
05443                 size_t nw;
05444                 char * t = rpmExpand(
05445                         "SQLite version ", sqlite3_libversion(), "\n",
05446 #if SQLITE_VERSION_NUMBER > 3006015
05447                         "\t(", sqlite3_sourceid(), ")\n",
05448 #endif
05449                         "Enter \".help\" for instructions\n",
05450                         "Enter SQL statements terminated with a \";\"\n", NULL);
05451                 nb = strlen(t);
05452                 nw = Fwrite(t, 1, nb, sql->ofd);
05453                 (void) Fflush(sql->ofd);
05454 assert(nb == nw);
05455                 t = _free(t);
05456 #if defined(HAVE_READLINE) && HAVE_READLINE==1
05457                 if (sql->zHistory)
05458                     read_history(sql->zHistory);
05459 #endif
05460                 oneshot++;
05461             }
05462 
05463             sql->ifd = Fdopen(fdDup(fileno(stdin)), "rb.fpio");
05464 assert(sql->ifd);
05465 
05466 sql->flags |= RPMSQL_FLAGS_PROMPT;
05467             rc = rpmsqlInput(sql);
05468 sql->flags &= ~RPMSQL_FLAGS_PROMPT;
05469 
05470             if (sql->ifd) (void) Fclose(sql->ifd);
05471             sql->ifd = _ifd;
05472 
05473             if (sql->zHistory) {
05474                 stifle_history(100);
05475                 write_history(sql->zHistory);
05476             }
05477             if (_ofd == NULL)
05478                 (void) Fclose(sql->ofd);
05479             sql->ofd = _ofd;
05480             sql->flags = _flags;
05481             if (rc != 0) rc = RPMRC_FAIL;
05482         } else
05483         if (!strcmp(s, "-") || !strcmp(s, "stdin")) {           /* STDIN */
05484 FD_t _ofd = sql->ofd;
05485 SQLDBG((stderr, "*** %s: STDIN\n", __FUNCTION__));
05486 
05487 if (sql->ofd == NULL) sql->ofd = fdDup(STDOUT_FILENO);
05488 assert(sql->ofd);
05489 
05490 assert(sql->ifd == NULL);
05491             sql->ifd = Fdopen(fdDup(fileno(stdin)), "rb.fpio");
05492 assert(sql->ifd);
05493 
05494             rc = rpmsqlInput(sql);
05495 
05496             if (sql->ifd) (void) Fclose(sql->ifd);
05497             sql->ifd = NULL;
05498 
05499 if (_ofd == NULL) (void) Fclose(sql->ofd);
05500             sql->ofd = _ofd;
05501 
05502             if (rc != 0) rc = RPMRC_FAIL;
05503         } else
05504         if (*s == '/') {                                /* FILE */
05505             FD_t _ifd = sql->ifd;
05506 SQLDBG((stderr, "*** %s: FILE\n", __FUNCTION__));
05507             sql->ifd = Fopen(s, "rb.fpio");
05508             if (!(sql->ifd == NULL || Ferror(sql->ifd))) {
05509                 rc = rpmsqlInput(sql);
05510             }
05511             if (sql->ifd) (void) Fclose(sql->ifd);
05512             sql->ifd = _ifd;
05513             if (rc != 0) rc = RPMRC_FAIL;
05514         } else {                                        /* STRING */
05515 SQLDBG((stderr, "*** %s: STRING\n", __FUNCTION__));
05516             if (*s == '.') {
05517                 char * t = xstrdup(s);
05518                 rc = rpmsqlMetaCommand(sql, t);
05519                 t = _free(t);
05520             } else {
05521                 sqlite3 * db;
05522                 char * zErrMsg = NULL;
05523                 _rpmsqlOpenDB(sql);
05524                 db = (sqlite3 *)sql->I;
05525                 rc = _rpmsqlShellExec(sql, s, _rpmsqlShellCallback, &zErrMsg);
05526                 if (zErrMsg) {
05527                     rpmsql_error(1, "%s", zErrMsg);
05528                     zErrMsg = _free(zErrMsg);
05529                     if (rc == 0) rc = RPMRC_FAIL;
05530                 } else if (rc != 0) {
05531                     rpmsql_error(1, _("unable to process SQL \"%s\""), s);
05532                     rc = RPMRC_FAIL;
05533                 }
05534             }
05535         }
05536             
05537         /* Return the SQL output. */
05538         if (sql->iob) {
05539             (void) rpmiobRTrim(sql->iob);
05540 SQLDBG((stderr, "==========>\n%s\n<==========\n", rpmiobStr(sql->iob)));
05541             if (resultp)
05542                 *resultp = rpmiobStr(sql->iob);         /* XXX strdup? */
05543         }
05544 
05545     }
05546 #endif  /* WITH_SQLITE */
05547 
05548 SQLDBG((stderr, "<== %s(%p,%p[%u]) rc %d\n", __FUNCTION__, sql, str, (unsigned)(str ? strlen(str) : 0), rc));
05549 
05550     return rc;
05551 }