# include "../ingres.h" # include "../aux.h" # include "../access.h" # include "../symbol.h" # include "../lock.h" # include "../fileio.h" # define BUFSIZE 1024 # define WBUFSIZE 512 # define MAXMAP 3 * MAXDOM # define DUMMY 'd' # define ESCAPE '\\' struct map { char name[MAXNAME+1]; /* attribute name */ char ftype; /* attfrmt of file domain */ char rtype; /* attfrmt of relation domain */ int flen; /* attfrml of file domain */ int rlen; /* attfrml of relation domain */ int roffset; /* attoff of relation domain */ int used; /* tag for duplicate checking */ char *fdelim; /* pointer to list of file param delims */ char *paramname; /* pointer to original parameter name */ /* used for supplying domain name in case of error */ }; struct map Map[MAXMAP]; /* one entry for each user specified domain in copy statement. */ int Mapcount; /* number of Map entries */ struct descriptor Des; /* descriptor for copied relation */ extern struct out_arg Out_arg; /* user defined formats for numeric output */ FILE *File_iop; /* i/o file pointer */ char Iobuf[IOBUFSIZ]; /* buffer for file i/o */ char *Filename; /* pointer to file name */ int Into; /* into is one if this is a copy into file */ char Inbuf[BUFSIZE]; /* input holder */ char Outbuf[BUFSIZE]; /* output holder */ long Tupcount; /* number of tuples processed */ char *Relname; /* name of relation */ long Duptuple; /* number of duplicate tuples */ long Baddoms; /* number of domains with control chars */ long Truncount; /* number of truncations on a c0 field */ int Piped[2]; /* pipe descriptor for copy communication */ char *Cpdomains[] /* dummy domain names for copy "into" */ { "nl", "\n", "tab", "\t", "sp", " ", "nul", "\0", "null", "\0", "comma", ",", "colon", ":", "dash", "-", "lparen", "(", "rparen", ")", 0 }; char Delimitor[] ",\n\t"; /* default delims for c0 & d0 */ /* ** COPY -- Performs an ingres COPY. ** ** History: ** 9/12/78 -- (marc) modified to print ** an error message when trying to ** copy into a view. ** For this the error number 5518 ** was added and the call on openr was split ** into two. */ copy(pc,pv) int pc; char **pv; { extern char *Usercode; extern int Noupdt; register int i, pid; register char *cp; int stat; int copydone(); int op; # ifdef xZTR1 if (tTf(7,1)) printf("entered copy\n"); # endif Duptuple = 0; Truncount = 0; Tupcount = 0; Baddoms = 0; Relname = pv[0]; Into = (pv[pc-2][0] == 'i'); Filename = pv[pc-1]; /* relation must exist and not be a system relation */ /* in addition a copy "from" can't be done if the user */ /* doesn't own the relation */ /* and furthermore it can't have an index */ i = 0; /* assume all is well */ if (op = openr(&Des, 2, Relname)) { if (op == AMOPNVIEW_ERR) i = 5818; else { if (op < 0) syserr("COPY: openr 1 (%.14s) %d", Relname, op); else /* non-existant relation */ i = 5800; } } else { if (Into) { if ((Des.relstat & S_PROTALL) && (Des.relstat & S_PROTRET) && !bequal(Usercode, Des.relowner, 2)) i = 5822; } else { /* extra checking if this is a copy "from" */ /* must be owned by the user */ if (!bequal(Usercode, Des.relowner, 2)) i = 5814; else /* must be updateable */ if ((Des.relstat & S_NOUPDT) && Noupdt) i = 5813; else /* must not be indexed */ if (Des.relindxd > 0) i = 5812; } } if (i) { closer(&Des); return (error(i, Relname, 0)); /* relation doesn't exist for this user */ } /* check that file name begins with a "/" */ cp = Filename; while (*cp == ' ') cp++; if (*cp != '/') { closer(&Des); return (error(5816, Filename, 0)); } /* fill map structures with transfer information */ if (i = mapfill(&pv[1])) { closer(&Des); return (i); /* error in user semantics */ } /* fork a child process which will run as the real user */ /* that child will complete the copy and exit */ if (pipe(Piped)) syserr("copy:can't make pipe"); if ((pid = fork()) < 0) syserr("copy:can't fork"); if (pid) { /* the ingres parent */ close(Piped[1]); ruboff(0); /* interrupts off */ stat = fullwait(pid, "copy"); if (read(Piped[0], &Des.reladds, 4) != 4) syserr("copy:can't read pipe"); close(Piped[0]); closer(&Des); /* close the rel */ rubon(); /* if stat is != 0 then add on 5800 for error */ if (stat) stat =+ 5800; return (stat); /* done */ } /* the child. change to run as the real user */ if (signal(2, 1) != 1) signal(2, copydone); /* clean up on rubout */ setuid(getuid()); # ifndef xB_UNIX setgid(getgid()); # endif if (Into) /* from relation into file */ { if ((File_iop = fopen(Filename, "write", Iobuf)) == NULL) /* create file for user */ i = error(5806, Filename, 0); /* cant create file */ else { if (Lockrel) /* set a shared lock on relation*/ setrll(A_SLP, Des.reltid, M_SHARE); i = rel_file(); } } else /* from UNIX file into relation */ { if ((File_iop = fopen(Filename, "read", Iobuf)) == NULL) i = error(5805, Filename, 0); /* cant open user file */ else { if (Lockrel) /* set an exclusive lock on relat*/ setrll(A_SLP, Des.reltid, M_EXCL); i = file_rel(); if (Duptuple) error(5819, locv(Duptuple), 0); /* warning only */ if (Baddoms) error(5820, locv(Baddoms), 0); /* warning only */ } } copydone(i); } copydone(i) int i; /* ** Finish up and exit after a copy or interrupt ** ** I is the return code. Since only a byte can be ** returned, only the least significant 2 decimal ** digits are returned. i is either 0 or a number like 58?? */ { if (Lockrel) /* unlock relation */ unlrl(Des.reltid); if (Truncount) error(5821, locv(Truncount), 0); /* warning only */ /* force the updates to be flushed */ cleanrel(&Des); if (File_iop) fclose(File_iop); if (write(Piped[1], &Des.reladds, 4) != 4) syserr("copyc:can't writepipe"); exit (i % 100); } rel_file() { int j; struct tup_id tid, limtid; char *cp, save; register int offset; register int i; register struct map *mp; /* set scan limits to scan the entire relation */ if (find(&Des, NOKEY, &tid, &limtid)) syserr("find error"); while ((i = get(&Des, &tid, &limtid, Inbuf, 1)) == 0) { mp = Map; offset = 0; for (i = 0; i < Mapcount; i++) { /* For cases of char to numeric conversion, there must be a null byte at the end of the string. The character just past the current domain is saved an a null byte inserted */ cp = &Inbuf[mp->roffset + mp->rlen]; /* compute address */ save = *cp; /* get the character */ *cp = '\0'; /* insert a null */ j = transfer(&Inbuf[mp->roffset], mp->rtype, mp->rlen, mp->ftype, mp->flen, offset); if (j) { /* bad ascii to numeric conversion or field length too small */ return (error(j, mp->paramname, &Inbuf[mp->roffset], locv(Tupcount), Relname, Filename, 0)); } *cp = save; /* restore the saved character */ offset =+ mp->flen; mp++; } Tupcount++; if (fwrite(File_iop, Outbuf, offset) != offset) syserr("copy:cant write to user file %s", Filename); } if (i < 0) syserr("bad get from rel %d", i); return (0); } file_rel() /* ** file_rel is called to transfer tuples from ** the input file and append them to the relation ** ** Char domains are initialized to blank and numeric ** domains are initialized to zero. */ { register int i, j; register struct map *mp; struct tup_id tid; clr_tuple(&Des, Outbuf); /* copy domains until an end of file or an error */ for (;;) { mp = Map; for (i = 0; i < Mapcount; i++) { if ((j = bread(mp)) <= 0) { if (j < 0) { i = 1; /* force an error */ j = 5815; /* unterminated string */ } else j = 5810; /* end of file */ if (i) /* error only if end of file during a tuple or unterminated string */ { i = error(j, mp->paramname, locv(Tupcount), Filename, Relname, 0); } return (i); } j = transfer(Inbuf, mp->ftype, mp->flen, mp->rtype, mp->rlen, mp->roffset); if (j) { /* bad ascii to numeric or field length too small */ return (error(j, mp->paramname, Inbuf, locv(Tupcount), Filename, Relname, 0)); } mp++; } Tupcount++; if ((j = insert(&Des, &tid, Outbuf, 1)) < 0) syserr("insert error %d rel=%s", j, Relname); if (j == 1) Duptuple++; mp++; } return (0); } transfer(in, sf, sl, df, dl, doff) char *in; /* pointer to input chars */ char sf; /* source format */ int sl; /* source length */ char df; /* destination format */ int dl; /* destination length */ int doff; /* destination offset */ /* ** transfer copies data from "*in" to ** Outbuf doing conversions whenever ** necessary */ { double d; float f; register char *inp, *outp; register int i; int j; long l; char temp[MAXFIELD]; /* holds char during conversions to ascii */ outp = &Outbuf[doff]; inp = in; if (sf == DUMMY) /* if source format is a dummy fields then nothing else need be done */ return (0); if (df == DUMMY) { /* fill field with dummy domain character */ i = dl; /* i equals the number of chars */ while (i--) *outp++ = sf; /* sf holds dummy char */ return (0); } if (sf != CHAR) { if (df == CHAR) /* numeric to char conversion */ { switch (sl) { /* int of size 1 or 2 */ case 1: itoa(*inp, temp); break; case 2: bmove(inp, &j, 2); /* copy to an integer */ itoa(j, temp); /* convert to ascii */ break; /* int or float of size 4 */ case 4: if (sf == INT) { bmove(inp, &l, 4); /* copy to a long */ smove(locv(l), temp); /* convert and copy */ } else { bmove(inp, &f, 4); ftoa(f, temp, dl, Out_arg.f4prec, Out_arg.f4style); } break; /* float of size 8 */ case 8: bmove(inp, &d, 8); /* copy to a dbl variable */ ftoa(d, temp, dl, Out_arg.f8prec, Out_arg.f8style); break; /* there is no possible default */ default: syserr("bad domain length %d",sl); } j = length(temp); if ((i = dl - j) < 0) return (5808); /* field won't fit */ /* blank pad from left. Number will be right justified */ while (i--) *outp++ = ' '; bmove(temp, outp, j); return (0); } if (convert(inp, outp, sf, sl, df, dl)) /* numeric to numeric transfer */ return (5808); /* numeric truncation error */ return (0); } /* character to numeric conversion */ /* and character to character conversion */ switch (df) { case CHAR: i = sl; if (!i) { i = length(inp); } if (i > dl) i = dl; if (charmove(inp, outp, i)) Baddoms++; for (outp =+ i; i