]> andersk Git - moira.git/blob - server/qvalidate.dc
*** empty log message ***
[moira.git] / server / qvalidate.dc
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
13 static 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>
21 EXEC SQL INCLUDE sqlca;
22 EXEC SQL INCLUDE sqlda;
23 #include "qrtn.h"
24
25 extern char *whoami;
26 extern int ingres_errno, mr_errcode;
27
28 EXEC SQL BEGIN DECLARE SECTION;
29 extern char stmt_buf[];
30 EXEC SQL END DECLARE SECTION;
31
32 EXEC SQL WHENEVER SQLERROR CALL ingerr;
33
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
40
41 /* Validation Routines */
42
43 validate_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
78 validate_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  */
166 static 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
185 validate_chars(s)
186 register char *s;
187 {
188     while (*s)
189       if (illegalchars[*s++])
190         return(MR_BAD_CHAR);
191     return(MR_EXISTS);
192 }
193
194
195 validate_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
263 validate_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
296 validate_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
314 validate_rename(argv, vo)
315 char *argv[];
316 struct 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
364 validate_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
393 validate_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;
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 */
420     if (dbg_index(data_type, ' '))
421         *dbg_index(data_type, ' ') = 0;
422     if (!strcmp(data_type, "user")) {
423         /* USER */
424         if (dbg_index(name, '@'))
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
481 lock_table(vo)
482 struct 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
499 sanity_check_database()
500 {
501 }
502
503
504 /* Dynamic SQL support routines */
505 MR_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++) {
522         if((it->sqlvar[j].sqldata=(char *)malloc(sizeof(short)+ARGLEN))==NULL) {
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  */
543 mr_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
566 /* Convert normal Unix-style wildcards to SQL voodoo */
567 convert_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  */
597 convert_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 */
624 mr_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  */
653 int 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.093447 seconds and 5 git commands to generate.