]> andersk Git - moira.git/blame - server/qvalidate.dc
Oracle and Solaris/POSIX changes
[moira.git] / server / qvalidate.dc
CommitLineData
73cf66ba 1/*
2 * $Source$
3 * $Author$
4 * $Header$
5 *
6 * Copyright (C) 1987 by the Massachusetts Institute of Technology
7 * For copying and distribution information, please see the file
8 * <mit-copyright.h>.
9 *
10 */
11
12#ifndef lint
13static char *rcsid_qsupport_dc = "$Header$";
14#endif lint
15
16#include <mit-copyright.h>
17#include <unistd.h>
18#include "query.h"
19#include "mr_server.h"
20#include <ctype.h>
21EXEC SQL INCLUDE sqlca;
22EXEC SQL INCLUDE sqlda;
23#include "qrtn.h"
24
25extern char *whoami;
26extern int ingres_errno, mr_errcode;
27
28EXEC SQL BEGIN DECLARE SECTION;
29extern char stmt_buf[];
30EXEC SQL END DECLARE SECTION;
31
32EXEC SQL WHENEVER SQLERROR CALL ingerr;
33
e448f08d 34#ifdef _DEBUG_MALLOC_INC
35#undef index
36#define dbg_index(str1,c) DBindex(__FILE__, __LINE__, str1, c)
37#else
38#define dbg_index index
39#endif
73cf66ba 40
41/* Validation Routines */
42
43validate_row(q, argv, v)
44 register struct query *q;
45 char *argv[];
46 register struct validate *v;
47{
48 EXEC SQL BEGIN DECLARE SECTION;
49 char *name;
50 char qual[128];
51 int rowcount;
52 EXEC SQL END DECLARE SECTION;
53
54 /* build where clause */
55 build_qual(v->qual, v->argc, argv, qual);
56
57 if (log_flags & LOG_VALID)
58 /* tell the logfile what we're doing */
59 com_err(whoami, 0, "validating row: %s", qual);
60
61 /* look for the record */
62 sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual);
63 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
64 if(sqlca.sqlcode)
65 return(MR_INTERNAL);
66 EXEC SQL DECLARE csr126 CURSOR FOR stmt;
67 EXEC SQL OPEN csr126;
68 EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA;
69 EXEC SQL CLOSE csr126;
70 rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
71
72 if (ingres_errno) return(mr_errcode);
73 if (rowcount == 0) return(MR_NO_MATCH);
74 if (rowcount > 1) return(MR_NOT_UNIQUE);
75 return(MR_EXISTS);
76}
77
78validate_fields(q, argv, vo, n)
79 struct query *q;
80 register char *argv[];
81 register struct valobj *vo;
82 register int n;
83{
84 register int status;
85
86 while (--n >= 0) {
87 switch (vo->type) {
88 case V_NAME:
89 if (log_flags & LOG_VALID)
90 com_err(whoami, 0, "validating %s in %s: %s",
91 vo->namefield, vo->table, argv[vo->index]);
92 status = validate_name(argv, vo);
93 break;
94
95 case V_ID:
96 if (log_flags & LOG_VALID)
97 com_err(whoami, 0, "validating %s in %s: %s",
98 vo->idfield, vo->table, argv[vo->index]);
99 status = validate_id(q, argv, vo);
100 break;
101
102 case V_DATE:
103 if (log_flags & LOG_VALID)
104 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
105 status = validate_date(argv, vo);
106 break;
107
108 case V_TYPE:
109 if (log_flags & LOG_VALID)
110 com_err(whoami, 0, "validating %s type: %s",
111 vo->table, argv[vo->index]);
112 status = validate_type(argv, vo);
113 break;
114
115 case V_TYPEDATA:
116 if (log_flags & LOG_VALID)
117 com_err(whoami, 0, "validating typed data (%s): %s",
118 argv[vo->index - 1], argv[vo->index]);
119 status = validate_typedata(q, argv, vo);
120 break;
121
122 case V_RENAME:
123 if (log_flags & LOG_VALID)
124 com_err(whoami, 0, "validating rename %s in %s",
125 argv[vo->index], vo->table);
126 status = validate_rename(argv, vo);
127 break;
128
129 case V_CHAR:
130 if (log_flags & LOG_VALID)
131 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
132 status = validate_chars(argv[vo->index]);
133 break;
134
135 case V_SORT:
136 status = MR_EXISTS;
137 break;
138
139 case V_LOCK:
140 status = lock_table(vo);
141 break;
142
143 case V_WILD:
144 status = convert_wildcards(argv[vo->index]);
145 break;
146
147 case V_UPWILD:
148 status = convert_wildcards_uppercase(argv[vo->index]);
149 break;
150
151 }
152
153 if (status != MR_EXISTS) return(status);
154 vo++;
155 }
156
157 if (ingres_errno) return(mr_errcode);
158 return(MR_SUCCESS);
159}
160
161
162/* validate_chars: verify that there are no illegal characters in
163 * the string. Legal characters are printing chars other than
164 * ", *, ?, \, [ and ].
165 */
166static int illegalchars[] = {
167 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
168 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
169 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
175 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
182 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
183};
184
185validate_chars(s)
186register char *s;
187{
188 while (*s)
189 if (illegalchars[*s++])
190 return(MR_BAD_CHAR);
191 return(MR_EXISTS);
192}
193
194
195validate_id(q, argv, vo)
196 struct query *q;
197 char *argv[];
198 register struct valobj *vo;
199{
200 EXEC SQL BEGIN DECLARE SECTION;
201 char *name, *tbl, *namefield, *idfield;
202 int id, rowcount;
203 EXEC SQL END DECLARE SECTION;
204 int status;
205 register char *c;
206
207 name = argv[vo->index];
208 tbl = vo->table;
209 namefield = vo->namefield;
210 idfield = vo->idfield;
211
212 if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) ||
213 !strcmp(tbl, "machine") ||
214 !strcmp(tbl, "subnet") ||
215 !strcmp(tbl, "filesys") ||
216 !strcmp(tbl, "list") ||
217 !strcmp(tbl, "cluster") ||
218 !strcmp(tbl, "strings")) {
219 if (!strcmp(tbl, "machine") || !strcmp(tbl, "subnet"))
220 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
221 status = name_to_id(name, tbl, &id);
222 if (status == 0) {
223 *(int *)argv[vo->index] = id;
224 return(MR_EXISTS);
225 } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") &&
226 (q->type == APPEND || q->type == UPDATE)) {
227 id=add_string(name);
228 cache_entry(name, "STRING", id);
229 *(int *)argv[vo->index] = id;
230 return(MR_EXISTS);
231 } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
232 return(vo->error);
233 else
234 return(status);
235 }
236
237 if (!strcmp(namefield, "uid")) {
238 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name);
239 } else {
240 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name);
241 }
242 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
243 if(sqlca.sqlcode)
244 return(MR_INTERNAL);
245 EXEC SQL DECLARE csr127 CURSOR FOR stmt;
246 EXEC SQL OPEN csr127;
247 rowcount=0;
248 EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
249 if(sqlca.sqlcode == 0) {
250 rowcount++;
251 EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
252 if(sqlca.sqlcode == 0) rowcount++;
253 }
254 EXEC SQL CLOSE csr127;
255 if (ingres_errno)
256 return(mr_errcode);
257
258 if (rowcount != 1) return(vo->error);
259 bcopy(SQLDA->sqlvar[0].sqldata,argv[vo->index],sizeof(int));
260 return(MR_EXISTS);
261}
262
263validate_name(argv, vo)
264 char *argv[];
265 register struct valobj *vo;
266{
267 EXEC SQL BEGIN DECLARE SECTION;
268 char *name, *tbl, *namefield;
269 int rowcount;
270 EXEC SQL END DECLARE SECTION;
271 register char *c;
272
273 name = argv[vo->index];
274 tbl = vo->table;
275 namefield = vo->namefield;
276 if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) {
277 for (c = name; *c; c++)
278 if (islower(*c))
279 *c = toupper(*c);
280 }
281 sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
282 tbl,tbl,namefield,name);
283 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
284 if(sqlca.sqlcode)
285 return(MR_INTERNAL);
286 EXEC SQL DECLARE csr128 CURSOR FOR stmt;
287 EXEC SQL OPEN csr128;
288 EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA;
289 rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
290 EXEC SQL CLOSE csr128;
291
292 if (ingres_errno) return(mr_errcode);
293 return ((rowcount == 1) ? MR_EXISTS : vo->error);
294}
295
296validate_date(argv, vo)
297 char *argv[];
298 struct valobj *vo;
299{
300 EXEC SQL BEGIN DECLARE SECTION;
301 char *idate;
302 double dd;
303 int errorno;
304 EXEC SQL END DECLARE SECTION;
305
306 idate = argv[vo->index];
307 EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
308
309 if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
310 return(MR_EXISTS);
311}
312
313
314validate_rename(argv, vo)
315char *argv[];
316struct valobj *vo;
317{
318 EXEC SQL BEGIN DECLARE SECTION;
319 char *name, *tbl, *namefield, *idfield;
320 int id;
321 EXEC SQL END DECLARE SECTION;
322 int status;
323 register char *c;
324
325 c = name = argv[vo->index];
326 while (*c)
327 if (illegalchars[*c++])
328 return(MR_BAD_CHAR);
329 tbl = vo->table;
330 /* minor kludge to upcasify machine names */
331 if (!strcmp(tbl, "machine"))
332 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
333 namefield = vo->namefield;
334 idfield = vo->idfield;
335 id = -1;
336 if (idfield == 0) {
337 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
338 return(MR_EXISTS);
339 sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = LEFT('%s',SIZE(%s))",
340 namefield,tbl,namefield,name,namefield);
341 EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
342 if(sqlca.sqlcode)
343 return(MR_INTERNAL);
344 EXEC SQL DECLARE csr129 CURSOR FOR stmt;
345 EXEC SQL OPEN csr129;
346 EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA;
347 if(sqlca.sqlcode == 0) id=1; else id=0;
348 EXEC SQL CLOSE csr129;
349
350 if (ingres_errno) return(mr_errcode);
351 if (id)
352 return(vo->error);
353 else
354 return(MR_EXISTS);
355 }
356 status = name_to_id(name, tbl, &id);
357 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
358 return(MR_EXISTS);
359 else
360 return(vo->error);
361}
362
363
364validate_type(argv, vo)
365 char *argv[];
366 register struct valobj *vo;
367{
368 EXEC SQL BEGIN DECLARE SECTION;
369 char *typename;
370 char *val;
371 int cnt;
372 EXEC SQL END DECLARE SECTION;
373 register char *c;
374
375 typename = vo->table;
376 c = val = argv[vo->index];
377 while (*c) {
378 if (illegalchars[*c++])
379 return(MR_BAD_CHAR);
380 }
381
382 /* uppercase type fields */
383 for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
384
385 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
386 WHERE name = :typename AND type='TYPE' AND trans = :val;
387 if (ingres_errno) return(mr_errcode);
388 return (cnt ? MR_EXISTS : vo->error);
389}
390
391/* validate member or type-specific data field */
392
393validate_typedata(q, argv, vo)
394 register struct query *q;
395 register char *argv[];
396 register struct valobj *vo;
397{
398 EXEC SQL BEGIN DECLARE SECTION;
399 char *name;
400 char *field_type;
401 char data_type[129];
402 int id;
403 EXEC SQL END DECLARE SECTION;
404 int status;
73cf66ba 405 register char *c;
406
407 /* get named object */
408 name = argv[vo->index];
409
410 /* get field type string (known to be at index-1) */
411 field_type = argv[vo->index-1];
412
413 /* get corresponding data type associated with field type name */
414 EXEC SQL SELECT trans INTO :data_type FROM alias
415 WHERE name = :field_type AND type='TYPEDATA';
416 if (ingres_errno) return(mr_errcode);
417 if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
418
419 /* now retrieve the record id corresponding to the named object */
e448f08d 420 if (dbg_index(data_type, ' '))
421 *dbg_index(data_type, ' ') = 0;
73cf66ba 422 if (!strcmp(data_type, "user")) {
423 /* USER */
e448f08d 424 if (dbg_index(name, '@'))
73cf66ba 425 return(MR_USER);
426 status = name_to_id(name, data_type, &id);
427 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
428 return(MR_USER);
429 if (status) return(status);
430 } else if (!strcmp(data_type, "list")) {
431 /* LIST */
432 status = name_to_id(name, data_type, &id);
433 if (status && status == MR_NOT_UNIQUE)
434 return(MR_LIST);
435 if (status == MR_NO_MATCH) {
436 /* if idfield is non-zero, then if argv[0] matches the string
437 * that we're trying to resolve, we should get the value of
438 * numvalues.[idfield] for the id.
439 */
440 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
441 set_next_object_id(q->validate->object_id, q->rtable, 0);
442 name = vo->idfield;
443 EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
444 WHERE name = :name;
445 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
446 } else
447 return(MR_LIST);
448 } else if (status) return(status);
449 } else if (!strcmp(data_type, "machine")) {
450 /* MACHINE */
451 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
452 status = name_to_id(name, data_type, &id);
453 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
454 return(MR_MACHINE);
455 if (status) return(status);
456 } else if (!strcmp(data_type, "string")) {
457 /* STRING */
458 status = name_to_id(name, data_type, &id);
459 if (status && status == MR_NOT_UNIQUE)
460 return(MR_STRING);
461 if (status == MR_NO_MATCH) {
462 if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
463 id=add_string(name);
464 cache_entry(name, "STRING", id);
465 } else if (status) return(status);
466 } else if (!strcmp(data_type, "none")) {
467 id = 0;
468 } else {
469 return(MR_TYPE);
470 }
471
472 /* now set value in argv */
473 *(int *)argv[vo->index] = id;
474
475 return (MR_EXISTS);
476}
477
478
479/* Lock the table named by the validation object */
480
481lock_table(vo)
482struct valobj *vo;
483{
484 sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0",
485 vo->table,vo->table,vo->idfield);
486 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
487 if (ingres_errno) return(mr_errcode);
488 if (sqlca.sqlerrd[2] != 1)
489 return(vo->error);
490 else
491 return(MR_EXISTS);
492}
493
494
495/* Check the database at startup time. For now this just resets the
496 * inprogress flags that the DCM uses.
497 */
498
499sanity_check_database()
500{
501}
502
503
504/* Dynamic SQL support routines */
505MR_SQLDA_T *mr_alloc_SQLDA()
506{
507 MR_SQLDA_T *it;
508 short *null_indicators;
509 register int j;
510
511 if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) {
512 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
513 exit(1);
514 }
515
516 if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) {
517 com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators");
518 exit(1);
519 }
520
521 for(j=0; j<QMAXARGS; j++) {
6612addc 522 if((it->sqlvar[j].sqldata=(char *)malloc(sizeof(short)+ARGLEN))==NULL) {
73cf66ba 523 com_err(whoami, MR_NO_MEM, "setting up SQLDA variables");
524 exit(1);
525 }
526 it->sqlvar[j].sqllen=ARGLEN;
527 it->sqlvar[j].sqlind=null_indicators+j;
528 null_indicators[j]=0;
529 }
530 it->sqln=QMAXARGS;
531 return it;
532}
533
534
535/* Use this after FETCH USING DESCRIPTOR one or more
536 * result columns may contain NULLs. This routine is
537 * not currently needed, since db/schema creates all
538 * columns with a NOT NULL WITH DEFAULT clause.
539 *
540 * This is currently dead flesh, since no Moira columns
541 * allow null values; all use default values.
542 */
543mr_fix_nulls_in_SQLDA(da)
544 MR_SQLDA_T *da;
545{
546 register IISQLVAR *var;
547 register int j;
548 int *intp;
549
550 for(j=0, var=da->sqlvar; j<da->sqld; j++, var++) {
551 switch(var->sqltype) {
552 case -IISQ_CHA_TYPE:
553 if(*var->sqlind)
554 *var->sqldata='\0';
555 break;
556 case -IISQ_INT_TYPE:
557 if(*var->sqlind) {
558 intp=(int *)var->sqldata;
559 *intp=0;
560 }
561 break;
562 }
563 }
564}
565
73cf66ba 566/* Convert normal Unix-style wildcards to SQL voodoo */
567convert_wildcards(arg)
568 char *arg;
569{
570 static char buffer[ARGLEN];
571 register char *s, *d;
572
573 for(d=buffer,s=arg;*s;s++) {
574 switch(*s) {
575 case '*': *d++='%'; *d++='%'; break;
576 case '?': *d++='_'; break;
577 case '_':
578 case '[':
579 case ']': *d++='*'; *d++ = *s; break;
580 case '%': *d++='*'; *d++='%'; *d++='%'; break;
581 default: *d++ = *s; break;
582 }
583 }
584 *d='\0';
585
586 /* Copy back into argv */
587 strcpy(arg,buffer);
588
589 return(MR_EXISTS);
590}
591
592/* This version includes uppercase conversion, for things like gmac.
593 * This is necessary because "LIKE" doesn't work with "uppercase()".
594 * Including it in a wildcard routine saves making two passes over
595 * the argument string.
596 */
597convert_wildcards_uppercase(arg)
598 char *arg;
599{
600 static char buffer[ARGLEN];
601 register char *s, *d;
602
603 for(d=buffer,s=arg;*s;s++) {
604 switch(*s) {
605 case '*': *d++='%'; *d++='%'; break;
606 case '?': *d++='_'; break;
607 case '_':
608 case '[':
609 case ']': *d++='*'; *d++ = *s; break;
610 case '%': *d++='*'; *d++='%'; *d++='%'; break;
611 default: *d++=toupper(*s); break; /* This is the only diff. */
612 }
613 }
614 *d='\0';
615
616 /* Copy back into argv */
617 strcpy(arg,buffer);
618
619 return(MR_EXISTS);
620}
621
622
623/* Looks like it's time to build an abstraction barrier, Yogi */
624mr_select_any(stmt)
625 EXEC SQL BEGIN DECLARE SECTION;
626 char *stmt;
627 EXEC SQL END DECLARE SECTION;
628{
629 int result=0;
630
631 EXEC SQL PREPARE stmt FROM :stmt;
632 EXEC SQL DESCRIBE stmt INTO :SQLDA;
633 if(SQLDA->sqld==0) /* Not a SELECT */
634 return(MR_INTERNAL);
635 EXEC SQL DECLARE csr CURSOR FOR stmt;
636 EXEC SQL OPEN csr;
637 EXEC SQL FETCH csr USING DESCRIPTOR :SQLDA;
638 if(sqlca.sqlcode==0)
639 result=MR_EXISTS;
640 else if((sqlca.sqlcode<0) && mr_errcode)
641 result=mr_errcode;
642 else
643 result=0;
644 EXEC SQL CLOSE csr;
645 return(result);
646}
647
648
649
650/* Adds a string to the string table. Returns the id number.
651 *
652 */
653int add_string(name)
654 EXEC SQL BEGIN DECLARE SECTION;
655 char *name;
656 EXEC SQL END DECLARE SECTION;
657{
658 EXEC SQL BEGIN DECLARE SECTION;
659 char buf[256];
660 int id;
661 EXEC SQL END DECLARE SECTION;
662
663 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
664 id++;
665 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
666
667 /* Use sprintf to get around problem with doubled single quotes */
668 sprintf(buf,"INSERT INTO strings (string_id, string) VALUES (%d, '%s')",id,name);
669 EXEC SQL EXECUTE IMMEDIATE :buf;
670
671 return(id);
672}
673
This page took 0.140137 seconds and 5 git commands to generate.