source: trunk/third/moira/server/qrtn.pc @ 24319

Revision 24319, 25.2 KB checked in by broder, 14 years ago (diff)
New Moira snapshot from SVN.
Line 
1/* $Id: qrtn.pc 3984 2010-02-18 22:34:45Z zacheiss $
2 *
3 * Query-processing routines
4 *
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 *
9 */
10
11#include <mit-copyright.h>
12#include "mr_server.h"
13#include "qrtn.h"
14#include "query.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <time.h>
20
21EXEC SQL INCLUDE sqlca;  /* SQL Communications Area */
22EXEC SQL INCLUDE sqlda;  /* SQL Descriptor Area */
23
24RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/server/qrtn.pc $ $Id: qrtn.pc 3984 2010-02-18 22:34:45Z zacheiss $");
25
26SQLDA *mr_sqlda;
27EXEC SQL BEGIN DECLARE SECTION;
28char stmt_buf[MR_STMTBUF_LEN];
29int proxy_acl;
30EXEC SQL END DECLARE SECTION;
31
32char *Argv[QMAXARGS];
33extern char *table_name[];
34extern char *sqlbuffer[QMAXARGS];
35
36int dbms_errno = 0;
37int mr_errcode = 0;
38EXEC SQL BEGIN DECLARE SECTION;
39int query_timeout = 30;
40char *database = "moira";
41EXEC SQL END DECLARE SECTION;
42extern char *whoami;
43extern FILE *journal;
44extern int QueryCount, max_version;
45extern struct query Queries[];
46
47/* Put this in a variable so that we can patch it if necessary */
48int max_row_count = 8192;
49
50int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]);
51int do_retrieve(struct query *q, char *pqual,
52                int (*action)(int, char *[], void *), void *actarg);
53int do_update(struct query *q, char *argv[], char *qual,
54              int (*action)(int, char *[], void *), void *actarg);
55int do_append(struct query *q, char *argv[], char *pqual,
56              int (*action)(int, char *[], void *), void *actarg);
57int do_delete(struct query *q, char *qual,
58              int (*action)(int, char *[], void *), void *actarg);
59void build_sql_stmt(char *result_buf, char *cmd, char *targetlist,
60                    char *argv[], char *qual);
61
62SQLDA *mr_alloc_sqlda(void);
63void sqlglm(char *, size_t *, size_t *);
64
65/*
66 * dbmserr: Called when the DBMS indicates an error.
67 */
68
69void dbmserr(void)
70{
71  EXEC SQL BEGIN DECLARE SECTION;
72  char err_msg[256];
73  EXEC SQL END DECLARE SECTION;
74  size_t bufsize, msglength;
75
76  dbms_errno = -sqlca.sqlcode;
77  mr_errcode = MR_DBMS_ERR;
78  com_err(whoami, MR_DBMS_ERR, " code %d\n", dbms_errno);
79  bufsize = sizeof(err_msg);
80  sqlglm(err_msg, &bufsize, &msglength);
81  err_msg[msglength] = 0;
82  com_err(whoami, 0, "SQL error text = %s", err_msg);
83  critical_alert(whoami, "MOIRA", "Moira server encountered DBMS ERROR %d\n%s",
84                 dbms_errno, err_msg);
85}
86
87/* This is declarative, not executed.  Applies from here on, in this file. */
88EXEC SQL WHENEVER SQLERROR DO dbmserr();
89
90int mr_open_database(void)
91{
92  int i;
93  static int first_open = 1;
94
95  if (first_open)
96    {
97      first_open = 0;
98
99      /* initialize local argv */
100      for (i = 0; i < QMAXARGS; i++)
101        Argv[i] = xmalloc(MAX_FIELD_WIDTH);
102
103      mr_sqlda = mr_alloc_sqlda();
104
105      incremental_init();
106    }
107
108  dbms_errno = 0;
109  mr_errcode = 0;
110
111  /* open the database */
112  EXEC SQL CONNECT :database IDENTIFIED BY :database;
113
114  if (dbms_errno)
115    return mr_errcode;
116
117  EXEC SQL SELECT list_id INTO :proxy_acl FROM capacls
118    WHERE capability = 'proxy';
119  if (dbms_errno)
120    return mr_errcode;
121
122  return MR_SUCCESS;
123}
124
125void mr_close_database(void)
126{
127  EXEC SQL COMMIT RELEASE;
128}
129
130int mr_check_access(client *cl, char *name, int argc, char *argv_ro[])
131{
132  struct query *q;
133
134  dbms_errno = 0;
135  mr_errcode = 0;
136
137  q = get_query_by_name(name, cl->version);
138  if (!q)
139    return MR_NO_HANDLE;
140
141  return mr_verify_query(cl, q, argc, argv_ro);
142}
143
144int mr_process_query(client *cl, char *name, int argc, char *argv_ro[],
145                     int (*action)(int, char *[], void *), void *actarg)
146{
147  struct query *q;
148  int status;
149  struct validate *v;
150  char *qual = NULL;
151  EXEC SQL BEGIN DECLARE SECTION;
152  char *table;
153  EXEC SQL END DECLARE SECTION;
154  struct save_queue *sq;
155
156  dbms_errno = 0;
157  mr_errcode = 0;
158
159  /* list queries command */
160  if (!strcmp(name, "_list_queries"))
161    {
162      list_queries(cl, action, actarg);
163      return MR_SUCCESS;
164    }
165
166  /* help query command */
167  if (!strcmp(name, "_help"))
168    {
169      if (argc < 1)
170        return MR_ARGS;
171      q = get_query_by_name(argv_ro[0], cl->version);
172      if (!q)
173        return MR_NO_HANDLE;
174      help_query(q, action, actarg);
175      return MR_SUCCESS;
176    }
177
178  /* get query structure, return error if named query does not exist */
179  q = get_query_by_name(name, cl->version);
180  if (!q)
181    return MR_NO_HANDLE;
182  v = q->validate;
183
184  /* setup argument vector, verify access and arguments */
185  if ((status = mr_verify_query(cl, q, argc, argv_ro)) != MR_SUCCESS)
186    goto out;
187
188  /* perform any special query pre-processing */
189  if (v && v->pre_rtn)
190    {
191      status = (*v->pre_rtn)(q, Argv, cl);
192      if (status != MR_SUCCESS)
193        goto out;
194    }
195
196  switch (q->type)
197    {
198    case MR_Q_RETRIEVE:
199      /* for queries that do not permit wildcarding, check if row
200         uniquely exists */
201      if (v && v->field)
202        {
203          status = validate_row(q, Argv, v);
204          if (status != MR_EXISTS)
205            break;
206        }
207
208      /* build "where" clause if needed */
209      if (q->qual)
210        qual = build_qual(q->qual, q->argc, Argv);
211
212      /* if there is a followup routine, then we must save the results */
213      /* of the first query for use by the followup routine */
214      /* if q->rvar = NULL, perform post_rtn only */
215      if (q->rvar)
216        {
217          if (v && v->post_rtn)
218            {
219              sq = sq_create();
220              status = do_retrieve(q, qual, sq_save_args, sq);
221              if (status != MR_SUCCESS)
222                {
223                  char **argv;
224                  int i;
225
226                  while (sq_get_data(sq, &argv))
227                    {
228                      for (i = 0; i < q->vcnt; i++)
229                        free(argv[i]);
230                      free(argv);
231                    }
232                  sq_destroy(sq);
233                  break;
234                }
235              status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
236            }
237          else
238            {
239              /* normal retrieve */
240              status = do_retrieve(q, qual, action, actarg);
241            }
242          if (status != MR_SUCCESS)
243            break;
244        }
245      else
246        status = (*v->post_rtn)(q, Argv, cl, action, actarg);
247
248      break;
249
250    case MR_Q_UPDATE:
251      /* see if row already exists */
252      if (v->field)
253        {
254          status = validate_row(q, Argv, v);
255          if (status != MR_EXISTS)
256            break;
257        }
258
259      /* build "where" clause and perform update */
260      /* if q->rvar = NULL, perform post_rtn only */
261      if (q->rvar)
262        {
263          qual = build_qual(q->qual, q->argc, Argv);
264          incremental_before(q->rtable, qual, argv_ro);
265          status = do_update(q, &Argv[q->argc], qual, action, actarg);
266          incremental_after(q->rtable, qual, argv_ro);
267          if (status != MR_SUCCESS)
268            break;
269          table = table_name[q->rtable];
270          if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif"))
271            {
272              EXEC SQL UPDATE tblstats
273                SET updates = updates + 1, modtime = SYSDATE
274                WHERE table_name = :table;
275            }
276        }
277
278      /* execute followup routine (if any) */
279      if (v->post_rtn)
280        status = (*v->post_rtn)(q, Argv, cl);
281
282      break;
283
284    case MR_Q_APPEND:
285      /* see if row already exists */
286      if (v->field)
287        {
288          status = validate_row(q, Argv, v);
289          if (status != MR_NO_MATCH)
290            break;
291        }
292
293      /* build "where" clause if needed */
294      if (q->qual)
295        qual = build_qual(q->qual, q->argc, Argv);
296
297      /* perform the append */
298      /* if q->rvar = NULL, perform post_rtn only */
299      if (q->rvar)
300        {
301          incremental_clear_before();
302          status = do_append(q, &Argv[q->argc], qual, action, actarg);
303          if (status != MR_SUCCESS)
304            break;
305          if (v && v->object_id)
306            {
307              qual = realloc(qual, 15 + strlen(q->rvar) +
308                             strlen(Argv[q->argc + q->vcnt]));
309              sprintf(qual, "%s.%s = %s", q->rvar, v->object_id,
310                      Argv[q->argc + q->vcnt]);
311              incremental_after(q->rtable, qual, argv_ro);
312            }
313          else
314            incremental_after(q->rtable, qual, argv_ro);
315
316          table = table_name[q->rtable];
317          EXEC SQL UPDATE tblstats
318            SET appends = appends + 1, modtime = SYSDATE
319            WHERE table_name = :table;
320        }
321
322      /* execute followup routine */
323      if (v->post_rtn)
324        status = (*v->post_rtn)(q, Argv, cl);
325      break;
326
327    case MR_Q_DELETE:
328      /* see if row already exists */
329      if (v->field)
330        {
331          status = validate_row(q, Argv, v);
332          if (status != MR_EXISTS)
333            break;
334        }
335
336      /* build "where" clause and perform delete */
337      /* if q->rvar = NULL, perform post_rtn only */
338      if (q->rvar)
339        {
340          qual = build_qual(q->qual, q->argc, Argv);
341          table = table_name[q->rtable];
342          incremental_before(q->rtable, qual, argv_ro);
343          status = do_delete(q, qual, action, actarg);
344          incremental_clear_after();
345          if (status != MR_SUCCESS)
346            break;
347          EXEC SQL UPDATE tblstats
348            SET deletes = deletes + 1, modtime = SYSDATE
349            WHERE table_name = :table;
350        }
351
352      /* execute followup routine */
353      if (v->post_rtn)
354        status = (*v->post_rtn)(q, Argv, cl);
355      break;
356
357    case MR_Q_SPECIAL:
358      break;
359    }
360
361out:
362  free(qual);
363
364  if (status == MR_SUCCESS && dbms_errno != 0)
365    {
366      com_err(whoami, MR_INTERNAL, "Server didn't notice DBMS ERROR %d",
367              dbms_errno);
368      status = mr_errcode;
369    }
370
371  if (q->type == MR_Q_RETRIEVE)
372    EXEC SQL COMMIT WORK;
373  else
374    {
375      if (status == MR_SUCCESS)
376        {
377          EXEC SQL COMMIT WORK;
378          if (journal)
379            {
380              char *buf;
381              int i;
382              extern time_t now;
383
384              fprintf(journal, "%% %s %s %s",
385                      cl->clname, cl->entity, ctime(&now));
386              fprintf(journal, "%s ", q->name);
387              for (i = 0; i < argc; i++)
388                {
389                  if (i != 0)
390                    putc(' ', journal);
391                  buf = requote(argv_ro[i]);
392                  fputs(buf, journal);
393                  free(buf);
394                }
395              putc('\n', journal);
396              fflush(journal);
397            }
398          incremental_update();
399        }
400      else
401        {
402          EXEC SQL ROLLBACK WORK;
403          incremental_flush();
404        }
405    }
406
407  if (status != MR_SUCCESS)
408    com_err(whoami, status, " (Query failed)");
409  return status;
410}
411
412char *build_qual(char *fmt_buf, int argc, char *argv[])
413{
414  char *res, *result_buf, *fmt, *arg, *like, *p;
415
416  result_buf = xmalloc(2 * (strlen(fmt_buf) + argc * ARGLEN));
417
418  res = result_buf;
419  fmt = fmt_buf;
420
421  like = strstr(fmt, "LIKE");
422  arg = strchr(fmt, '%');
423
424  /* Look through the format for LIKE expressions and arguments.
425     Substitute in the arguments, simplify the `LIKE's to `='s
426     where possible, and insert ESCAPE clauses where needed */
427
428  while (*fmt)
429    {
430     
431      if ((!like && !arg) || argc == 0)
432        {
433          /* only plain text remains */
434          strcpy(res, fmt);
435          res = strchr(res, '\0');
436          break;
437        }
438      else if (!like || arg < like)
439        {
440          /* regular arg: copy up to arg, then substitute */
441          strncpy(res, fmt, arg - fmt);
442          res += arg - fmt;
443          if (*++arg)
444            {
445              switch (*arg++)
446                {
447                case '%':
448                  *res++ = '%';
449                  break;
450
451                case 's':
452                  p = *argv;
453                  /* copy string, doubling single quotes */
454                  while (*p)
455                    {
456                      if (*p == '\'')
457                        *res++ = '\'';
458                      *res++ = *p++;
459                    }
460                  argv++;
461                  break;
462
463                case 'd':
464                  res += sprintf(res, "%d", *(int *)*argv++);
465                  break;
466                }
467            }
468          fmt = arg;
469          arg = strchr(fmt, '%');
470        } else {
471          /* LIKE arg: copy over up to the arg, then copy and convert arg */
472          int escape = 0, pattern = 0;
473          char *likepos = res + (like - fmt);
474
475          strncpy(res, fmt, arg - fmt);
476          res += arg - fmt;
477
478          /* copy arg, converting UNIX globs to `SQL voodoo', and noting
479             if we'll need an ESCAPE clause */
480          for (p = *argv++; *p; p++)
481            {
482              switch (*p)
483                {
484                case '*':
485                  *res++ = '%';
486                  *res++ = '%'; /* need to double for build_sql_stmt */
487                  pattern = 1;
488                  break;
489
490                case '?':
491                  *res++ = '_';
492                  pattern = 1;
493                  break;
494
495                case '%':
496                case '_':
497                  *res++ = '*';
498                  *res++ = *p;
499                  if (*p == '%')
500                    *res++ = *p;
501                  escape = 1;
502                  break;
503
504                case '\'':
505                  *res++ = '\'';
506                  /* fall through */
507
508                default:
509                  *res++ = *p;
510                }
511            }
512
513          /* if no pattern characters, write over "LIKE" with " =  " */
514          if (!pattern && !escape)
515            memcpy(likepos, " =  ", 4);
516
517          fmt = arg + 2;
518          while (*fmt && *fmt != ' ')
519            *res++ = *fmt++;
520
521          if (escape)
522            res += sprintf(res, " ESCAPE '*'");
523
524          arg = strchr(fmt, '%');
525          like = strstr(fmt, "LIKE");
526        }
527    }
528
529  *res = '\0';
530  result_buf = realloc(result_buf, strlen(result_buf) + 1);
531  return result_buf;
532}
533
534/* Build arguement vector, verify query and arguments */
535
536int privileged;
537
538int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[])
539{
540  int argreq;
541  int status;
542  struct validate *v = q->validate;
543  int i;
544  char *to, *fr, *stop;
545
546  privileged = 0;
547
548  /* check argument count */
549  argreq = q->argc;
550  if (q->type == MR_Q_UPDATE || q->type == MR_Q_APPEND)
551    argreq += q->vcnt;
552  if (argc != argreq)
553    return MR_ARGS;
554
555  /* copy the arguments into a local argv that we can modify */
556  for (i = 0; i < argc; i++)
557    {
558      for (to = Argv[i], fr = argv_ro[i], stop = to + MAX_FIELD_WIDTH; (*fr) && (to < stop);)
559        *to++ = *fr++;
560
561      if (*fr)
562        return MR_ARG_TOO_LONG;
563      *to = '\0';
564    }
565
566  /* Check initial query access.  If we're acting as a proxy, only allow
567   * access if the query has "default" as a capacl.
568   */
569  status = check_query_access(q, Argv, cl);
570  if (status != MR_SUCCESS && status != MR_PERM)
571    return status;
572  if (status == MR_SUCCESS && (!cl->proxy_id || q->everybody))
573      privileged++;
574
575  /* validate arguments */
576  if (v && v->valobj)
577    {
578      status = validate_fields(q, Argv, v->valobj, v->objcnt);
579      if (status != MR_SUCCESS)
580        return status;
581    }
582
583  /* perform special query access check */
584  if (!privileged && v && v->acs_rtn)
585    {
586      status = (*v->acs_rtn)(q, Argv, cl);
587      if (status != MR_SUCCESS && status != MR_PERM)
588        return status;
589      if (status == MR_SUCCESS)
590        return MR_SUCCESS;
591    }
592
593  return privileged ? MR_SUCCESS : MR_PERM;
594}
595
596int check_query_access(struct query *q, char *argv[], client *cl)
597{
598  EXEC SQL BEGIN DECLARE SECTION;
599  char *name;
600  int acl_id;
601  static int def_uid;
602  EXEC SQL END DECLARE SECTION;
603
604  /* initialize default uid */
605  if (def_uid == 0)
606    EXEC SQL SELECT users_id INTO :def_uid FROM users WHERE login = 'default';
607
608  name = q->shortname;
609  EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name;
610  if (sqlca.sqlcode < 0)
611    return MR_DBMS_ERR;
612  if (sqlca.sqlcode == SQL_NO_MATCH)
613    return MR_PERM;
614  q->acl = acl_id;
615 
616  /* check for default access */
617  EXEC SQL SELECT member_id INTO :acl_id FROM imembers
618    WHERE list_id = :acl_id AND member_type = 'USER'
619    AND member_id = :def_uid;
620  if (sqlca.sqlerrd[2] == 0)
621    q->everybody = 0;
622  else
623    q->everybody = 1;
624
625  if (q->everybody)
626    return MR_SUCCESS;
627
628  if (find_member("LIST", acl_id, cl))
629    return MR_SUCCESS;
630  else
631    return MR_PERM;
632}
633
634int find_member(char *list_type, int list_id, client *cl)
635{
636  EXEC SQL BEGIN DECLARE SECTION;
637  int flag, users_id, client_id;
638  EXEC SQL END DECLARE SECTION;
639
640  if (!strcmp(strtrim(list_type), "USER") && list_id == cl->users_id)
641    return 1;
642
643  if (!strcmp(strtrim(list_type), "KERBEROS") && list_id == -cl->client_id)
644    return 1;
645
646  if (!strcmp(strtrim(list_type), "LIST"))
647    {
648      /* see if client is a member of list */
649      flag = 0;
650      users_id = cl->users_id;
651      client_id = -cl->client_id;
652      EXEC SQL SELECT COUNT(member_id) INTO :flag FROM imembers
653        WHERE list_id = :list_id
654        AND ( ( member_type = 'USER' AND member_id = :users_id )
655              OR (member_type = 'KERBEROS' AND member_id = :client_id ) );
656      if (sqlca.sqlcode == 0)
657        return flag;
658    }
659
660  return 0;
661}
662
663
664int do_retrieve(struct query *q, char *pqual,
665                int (*action)(int, char *[], void *), void *actarg)
666{
667  build_sql_stmt(stmt_buf, "SELECT", q->tlist, NULL, pqual);
668  if (q->sort)
669    {
670      strcat(stmt_buf, " ORDER BY ");
671      strcat(stmt_buf, q->sort);
672    }
673
674  return do_for_all_rows(stmt_buf, q->vcnt, action, actarg);
675}
676
677void build_sql_stmt(char *result_buf, char *cmd, char *targetlist,
678                    char *argv[], char *qual)
679{
680  char fmt_buf[MR_STMTBUF_LEN];
681  char *res, *fmt;
682
683  if (qual)
684    sprintf(fmt_buf, "%s %s WHERE %s", cmd, targetlist, qual);
685  else
686    sprintf(fmt_buf, "%s %s", cmd, targetlist);
687
688  for (res = result_buf, fmt = fmt_buf; *fmt; fmt++)
689    {
690      if (*fmt == '%')
691        {
692          if (*++fmt)
693            {
694              switch (*fmt)
695                {
696                case '%':                       /* %% -> % */
697                  *res++ = *fmt;
698                  break;
699                case 's':
700                  if (*argv[0])
701                    {
702                      char *p = *argv;
703                      while (*p)
704                        {
705                          if (*p == '\'')
706                            *res++ = '\'';      /* double the ' */
707                          *res++ = *p++;
708                        }
709                    }
710                  argv++;
711                  break;
712                case 'd':
713                  res += sprintf(res, "%d", *(int *)*argv++);
714                  break;
715                default:                        /* Swallow other %? pairs */
716                  break;
717                }
718            }
719          else
720            break;
721        }
722      else
723        *res++ = *fmt;                          /* text -> result buffer */
724    }
725  *res = '\0';
726}
727
728int do_update(struct query *q, char *argv[], char *qual,
729              int (*action)(int, char *[], void *), void *actarg)
730{
731  build_sql_stmt(stmt_buf, "UPDATE", q->tlist, argv, qual);
732  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
733  if (mr_errcode)
734    return mr_errcode;
735  return MR_SUCCESS;
736}
737
738int do_append(struct query *q, char *argv[], char *pqual,
739              int (*action)(int, char *[], void *), void *actarg)
740{
741  build_sql_stmt(stmt_buf, "INSERT", q->tlist, argv, pqual);
742  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
743  if (mr_errcode)
744    return mr_errcode;
745  return MR_SUCCESS;
746}
747
748int do_delete(struct query *q, char *qual,
749              int (*action)(int, char *[], void *), void *actarg)
750{
751  sprintf(stmt_buf, "DELETE FROM %s WHERE %s", table_name[q->rtable], qual);
752  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
753  if (mr_errcode)
754    return mr_errcode;
755  return MR_SUCCESS;
756}
757
758
759/**
760 ** set_next_object_id - set next object id in values table
761 **
762 ** Inputs: object - object name in values table and in objects
763 **         table - name of table objects are found in
764 **         limit - should the ID be range limited
765 **
766 ** - called before an MR_Q_APPEND operation to set the next object id to
767 **   be used for the new record to the next free value
768 **
769 **/
770
771int set_next_object_id(char *object, enum tables table, int limit)
772{
773  EXEC SQL BEGIN DECLARE SECTION;
774  int value;
775  char *obj = object;
776  EXEC SQL END DECLARE SECTION;
777  int starting_value;
778
779  EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :obj;
780  if (sqlca.sqlerrd[2] != 1)
781    return MR_NO_ID;
782
783  starting_value = value;
784  while (1)
785    {
786#ifdef ULTRIX_ID_HOLE
787      if (limit && value > 31999 && value < 32768)
788        value = 32768;
789#endif
790      if (limit && value > MAX_ID_VALUE)
791        value = MIN_ID_VALUE;
792
793      sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = %d",
794              object, table_name[table], object, value);
795      dosql(sqlbuffer);
796      if (sqlca.sqlcode < 0)
797        return mr_errcode;
798      if (sqlca.sqlcode == SQL_NO_MATCH)
799        break;
800
801      value++;
802      if (limit && value == starting_value)
803        {
804          com_err(whoami, 0, "All id values have been used");
805          return MR_NO_ID;
806        }
807    }
808
809  com_err(whoami, 0, "setting ID %s to %d", object, value);
810  EXEC SQL UPDATE numvalues SET value = :value WHERE name = :obj;
811  return MR_SUCCESS;
812}
813
814
815/* Turn a kerberos name into the user's ID of the account that principal
816 * owns.  Sets the kerberos ID and user ID.
817 */
818
819int set_krb_mapping(char *name, char *login, int ok, int *kid, int *uid)
820{
821  EXEC SQL BEGIN DECLARE SECTION;
822  int u_id, k_id;
823  char *krbname;
824  EXEC SQL END DECLARE SECTION;
825
826  krbname = name;
827  *kid = 0;
828  *uid = 0;
829
830  EXEC SQL SELECT km.users_id, km.string_id INTO :u_id, :k_id
831    FROM krbmap km, strings str
832    WHERE km.string_id = str.string_id AND str.string = :krbname;
833  EXEC SQL COMMIT WORK;
834
835  if (dbms_errno)
836    return mr_errcode;
837
838  if (sqlca.sqlerrd[2] == 1)
839    {
840      *kid = -k_id;
841      *uid = u_id;
842      return MR_SUCCESS;
843    }
844
845  if (name_to_id(name, STRINGS_TABLE, &k_id) == MR_SUCCESS)
846    *kid = -k_id;
847
848  if (!ok)
849    {
850      *uid = *kid;
851      return MR_SUCCESS;
852    }
853
854  if (name_to_id(login, USERS_TABLE, uid) != MR_SUCCESS)
855    *uid = 0;
856
857  if (*kid == 0)
858    *kid = *uid;
859  if (dbms_errno)
860    return mr_errcode;
861  return MR_SUCCESS;
862}
863
864
865void sanity_check_queries(void)
866{
867  int i;
868  int maxv = 0, maxa = 0;
869
870#define MAX(x, y) ((x) > (y) ? (x) : (y))
871
872  for (i = 0; i < QueryCount; i++)
873    {
874      maxv = MAX(maxv, Queries[i].vcnt);
875      maxa = MAX(maxa, Queries[i].argc);
876      max_version = MAX(max_version, Queries[i].version);
877    }
878  if (MAX(maxv, maxa) > QMAXARGS)
879    {
880      com_err(whoami, 0, "A query has more args than QMAXARGS");
881      exit(1);
882    }
883}
884
885
886/* Generically do a SELECT, storing the results in the provided buffers */
887
888void dosql(char *buffers[])
889{
890  int i, errcode = 0, errlen;
891
892  EXEC SQL PREPARE inc_stmt FROM :stmt_buf;
893  if (sqlca.sqlcode)
894    return;
895  EXEC SQL DECLARE inc_crs CURSOR FOR inc_stmt;
896  EXEC SQL OPEN inc_crs;
897  mr_sqlda->N = QMAXARGS;
898  EXEC SQL DESCRIBE SELECT LIST FOR inc_stmt INTO mr_sqlda;
899  mr_sqlda->N = mr_sqlda->F;
900  for (i = 0; i < mr_sqlda->N; i++)
901    {
902      mr_sqlda->V[i] = buffers[i];
903      mr_sqlda->T[i] = 97;
904      mr_sqlda->L[i] = MAX_FIELD_WIDTH;
905    }
906  EXEC SQL FETCH inc_crs USING DESCRIPTOR mr_sqlda;
907
908  /* if we got an error from the FETCH, we have to preserve it or the
909     close will reset it and the caller will think nothing happened */
910  if (sqlca.sqlcode)
911    {
912      errcode = sqlca.sqlcode;
913      errlen = sqlca.sqlerrm.sqlerrml;
914    }
915
916  EXEC SQL CLOSE inc_crs;
917  if (errcode)
918    {
919      sqlca.sqlcode = errcode;
920      sqlca.sqlerrm.sqlerrml = errlen;
921    }
922}
923
924int do_for_all_rows(char *query, int count,
925                    int (*action)(int, char *[], void *), void *actarg)
926{
927  int i, rowcount = 0;
928  EXEC SQL BEGIN DECLARE SECTION;
929  char *q = query;
930  EXEC SQL END DECLARE SECTION;
931
932  EXEC SQL PREPARE stmt FROM :q;
933  if (sqlca.sqlcode)
934    return MR_INTERNAL;
935  EXEC SQL DECLARE curs CURSOR FOR stmt;
936  EXEC SQL OPEN curs;
937  mr_sqlda->N = count;
938  EXEC SQL DESCRIBE SELECT LIST FOR stmt INTO mr_sqlda;
939  mr_sqlda->N = mr_sqlda->F;
940  for (i = 0; i < mr_sqlda->N; i++)
941    {
942      mr_sqlda->V[i] = sqlbuffer[i];
943      mr_sqlda->T[i] = 97;
944      mr_sqlda->L[i] = MAX_FIELD_WIDTH;
945    }
946
947  while (rowcount < max_row_count)
948    {
949      EXEC SQL FETCH curs USING DESCRIPTOR mr_sqlda;
950      if (sqlca.sqlcode)
951        break;
952      (*action)(count, sqlbuffer, actarg);
953      rowcount++;
954    }
955  EXEC SQL CLOSE curs;
956
957  if (mr_errcode)
958    return mr_errcode;
959  if (rowcount == max_row_count)
960    {
961      critical_alert(whoami, "moirad", "attempted query with too many rows");
962      return MR_NO_MEM;
963    }
964  else if (rowcount == 0)
965    return MR_NO_MATCH;
966  else
967    return MR_SUCCESS;
968}
969
970/* Do a name to ID translation.  Moved from cache.pc because we did away
971 * with the cache, but what this function does is still useful to us.
972 */
973
974int name_to_id(char *name, enum tables type, int *id)
975{
976  EXEC SQL BEGIN DECLARE SECTION;
977  char *iname;
978  int j;
979  EXEC SQL END DECLARE SECTION;
980
981  iname = name;
982
983  switch (type)
984    {
985    case USERS_TABLE:
986      if (strchr(iname, '@') || (strlen(iname) > 8))
987        {
988          sqlca.sqlcode = SQL_NO_MATCH;
989          break;
990        }
991      EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname;
992      break;
993    case LIST_TABLE:
994      EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname;
995      break;
996    case MACHINE_TABLE:
997      EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname);
998      break;
999    case SUBNET_TABLE:
1000      EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname);
1001      break;
1002    case CLUSTERS_TABLE:
1003      EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname;
1004      break;
1005    case FILESYS_TABLE:
1006      EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname;
1007      break;
1008    case STRINGS_TABLE:
1009      if (!iname[0])    /* special-case empty string */
1010        {
1011          *id = 0;
1012          return MR_SUCCESS;
1013        }
1014      EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname;
1015      break;
1016    case CONTAINERS_TABLE:
1017      EXEC SQL SELECT cnt_id INTO :j FROM containers WHERE LOWER(name) =
1018        LOWER(:iname);
1019      break;
1020    default:
1021      return MR_INTERNAL;
1022    }
1023  if (sqlca.sqlcode == SQL_NO_MATCH)
1024    return MR_NO_MATCH;
1025  if (sqlca.sqlerrd[2] > 1)
1026    return MR_NOT_UNIQUE;
1027  if (sqlca.sqlcode)
1028    return MR_DBMS_ERR;
1029  *id = j;
1030
1031  return MR_SUCCESS;
1032}
1033
1034/* Perform an ID to name mapping.  name should be a pointer to a pointer to
1035 * malloc'ed data.  The buffer it refers to will be freed, and a new buffer
1036 * allocated with the answer.
1037 *
1038 * This used to be in cache.pc, but we've removed the cache, and this function
1039 * is still useful to us.
1040 */
1041
1042int id_to_name(int id, enum tables type, char **name)
1043{
1044  EXEC SQL BEGIN DECLARE SECTION;
1045  char iname[MAX_FIELD_WIDTH];
1046  int j;
1047  EXEC SQL END DECLARE SECTION;
1048
1049  j = id;
1050
1051  switch (type)
1052    {
1053    case USERS_TABLE:
1054      EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j;
1055      break;
1056    case LIST_TABLE:
1057      EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j;
1058      break;
1059    case MACHINE_TABLE:
1060      EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j;
1061      break;
1062    case SUBNET_TABLE:
1063      EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j;
1064      break;
1065    case CLUSTERS_TABLE:
1066      EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j;
1067      break;
1068    case FILESYS_TABLE:
1069      EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j;
1070      break;
1071    case STRINGS_TABLE:
1072      EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j;
1073      break;
1074    case CONTAINERS_TABLE:
1075      EXEC SQL SELECT name INTO :iname FROM containers WHERE cnt_id = :j;
1076      break;
1077    default:
1078      return MR_INTERNAL;
1079    }
1080  if (sqlca.sqlcode == SQL_NO_MATCH)
1081    {
1082      free(*name);
1083      sprintf(iname, "#%d", j);
1084      *name = xstrdup(iname);
1085      return MR_NO_MATCH;
1086    }
1087  if (sqlca.sqlerrd[2] > 1)
1088    return MR_INTERNAL;
1089  if (sqlca.sqlcode)
1090    return MR_DBMS_ERR;
1091  free(*name);
1092  *name = xstrdup(strtrim(iname));
1093
1094  return MR_SUCCESS;
1095}
1096
1097/* eof:qrtn.dc */
Note: See TracBrowser for help on using the repository browser.