Document #: US-54302,EN ------------------------------------------------------------------------------ Author: Alexander Hamilton Abstract: How to perform "in memory" COPY FROM/INTO. This is an UNSUPPORTED feature for 6.3/03 and 6.4 Unix and VMS C only. A SUPPORTED syntax will be added in a future release supporting all languages. Interim Support for COPY FROM/INTO Memory ----------------------------------------- This outlines an INGRES 6.3/03 & 6.4 feature: COPY FROM/INTO PROGRAM. It is available in C on Unix and VMS only. In a future release, this syntax will be replaced by a new syntax supported in all languages. Using COPY FROM/INTO PROGRAM in an application allows you to copy data from or to memory. It provides the quickest way to bulk load data. The COPY statement you write differs by declaring a user-coded handler in the COPY statement WITH clause. On a COPY INTO PROGRAM statement, COPY will call the user handler passing each row it receives from the dbms. On a COPY FROM PROGRAM, COPY will call the handler for the next row of data until the handler indicates that there are no more rows. The syntax of COPY FROM/INTO PROGRAM is as follows: ESQL: EXEC SQL COPY TABLE tabname ([format_string]) FROM|INTO PROGRAM WITH COPYHANDLER = func_name; EQUEL: ## COPY tabname ([format_string]) FROM|INTO PROGRAM WITH COPYHANDLER = func_name; The COPYHANDLER option on the WITH clause tells INGRES the name of the function to call to get/put a row of data. 'func_name' must be a user- defined function. Although you should not declare 'func_name' in an ESQL declare section or through an EQUEL ## mark, it must be declared in a way that will satisfy the C compiler. Other WITH clause options accepted by the COPY FROM/INTO file may be used on the COPY FROM/INTO PROGRAM statement. (See note on "WITH ERROR = CONTINUE" below). All types of row formats are acceptable on COPY FROM/INTO PROGRAM. The character formats (variable or fixed) are the easiest to program. If numeric formats are used, remember that numeric entities are not aligned on any special boundary in the row. This is significant on Unix. On COPY INTO PROGRAM, a handler on Unix must byte-copy numerics into aligned memory before assigning the values. ON COPY FROM PROGRAM, the handler must byte copy numeric formats into the row buffer: they cannot be directly assigned because of alignment problems. One of the examples below illustrates this. For fixed length formats, the length and characteristics of the data are as follows. On a COPY INTO, this describes how the data is formatted by INGRES into the row buffer handed to the user-defined handler; on a COPY FROM, the user-defined handler must format the data according to these formats. integer 4 bytes integer with null 5 bytes float 8 bytes float with null 9 bytes character(n) n bytes character(n) with null n + 1 bytes character(n) with null('value') n bytes varchar(n) n + 5 bytes for the count (char representation) varchar(n) with null n + 1 + 5 bytes varchar(n) with null('value') n + 5 bytes For variable length formats (char(0) and varchar(0)), the data may be of any length. Varchar(0) is self describing because the data is preceded by a 5-byte count. A delimiter must be used with char(0). For bulk copy, the formats for integer, float and character are the same as fixed length. The varchar format differs in that the count is represented in internal format, i,e., as a 2-byte integer quantity. The formats for date and money are internal and are not described here. It is assumed that a user-defined handler will use a character copy format for date and a character or float format for money. The user defined handlers for COPY FROM PROGRAM and COPY INTO PROGRAM each take three parameters and must return an integer status of 0 to INGRES to indicate success. If a handler returns a non-zero status, INGRES will raise an error and abort COPY. (The "on_error = continue" clause does not apply to reading/writing to a file or a user program -- it applies to row->tuple and tuple->row conversions in COPY). The arguments passed by COPY must be declared in the user-defined handler with the following types: Argument Type Description COPY TO PROGRAM byte_length Pointer to long (Input) Byte length of row row Pointer to char (Input) A row of data dummy Pointer to long Currently unused COPY FROM PROGRAM byte_length Pointer to long (Input). For fixed length formats indicates the expected byte count of the user-supplied row. For variable length, indicates the maximum length of row. row Pointer to char (Output) Pointer to row buffer that handler fills. bytes_used Pointer to long Number of bytes of row filled by handler. If bytes_used is set to zero, COPY does not call handler again. On fixed length formats, INGRES raises an error if a non-zero bytes_used is not equal to byte_length. On variable length formats a non-zero bytes_used is ignored. The following are some examples of handlers coded in C. These examples show how to move data between host variables and the tuple buffer. The use of COPY handlers is available in C only. Example 1 /* ** Copy into program using bulk format ** Table contains: ** integer ** char(20) ** float ** varchar(20) ** The user-defined handler parses the tuple and stores each column in a ** variable; then prints the whole row. */ main() { int put_row(); exec sql begin declare section; exec sql end declare section; exec sql connect dbname; exec sql drop table t; /* For test purposes create table and insert three rows of data */ exec sql create table t (col1 integer, col2 char(20), col3 float, col4 varchar(20)); exec sql insert into t values (1, 'this is row one', 1.1, 'row one varchar'); exec sql insert into t values (2, 'this is row two', 2.2, 'row two varchar'); exec sql insert into t values (3, 'this is row three', 3.3, 'row three varchar'); exec sql copy table t () into program with copyhandler = put_row; exec sql disconnect; } int put_row(byte_length, row, dummy) int *byte_length; char *row; int *dummy; { char *fromp, *top; /* Byte pointers for copying row */ int i; int col1; /* Variables corresponding to columns */ char col2[21]; double col3; short col4len; char col4[21]; fromp = row; /* Read off integer col1 */ top = (char *)&col1; for (i = 1; i <=sizeof(int); i++) *top++ = *fromp++; fromp++; /* Skip null indicator byte */ /* Read off char(20) col2 */ for (i = 0; i < 20; i++) col2[i] = *fromp++; col2[20] = '\0'; fromp++; /* Skip null indicator byte */ /* Read off float col3 */ top = (char *)&col3; for (i = 1; i <= sizeof(double); i++) *top++ = *fromp++; fromp++; /* Skip null indicator byte */ /* Read off varchar col4 -- 5-char count first */ top = (char *)&col4len; for (i = 1; i <=sizeof(short); i++) *top++ = *fromp++; /* Read off varchar text col4 according to count */ for (i = 0; i < col4len; i++) col4[i] = *fromp++; col4[col4len] = '\0'; /* Skip remaining bytes of varchar(20) */ for (i = 20-col4len; i > 0; i--) fromp++; fromp++; /* Skip null indicator byte */ printf ("Row is %d %s %f %d (count) %s\n", col1, col2, col3, col4len, col4); /* Number of bytes read accurate? */ if (fromp - row != *byte_length) return 1; return 0; } Example 2: /* ** Copy from program using fixed length formats. ** Table contains: ** integer ** char(20) ** float ** date ** ** Copy uses formats: ** integer ** char(20) ** float8 ** char(25) ** ** The user-defined handler copies data from a structure into the tuple ** buffer. Because formats are not nullable, the handler does not have ** to place a "null terminator" byte or other null indicator into the ** buffer. */ /* Declare some "canned" data */ struct { int col1; char col2[21]; double col3; char col4[25]; } get_data[] = { { 1,"this is the 1st row ", 1.1, "21-mar-1991 "}, { 2,"this is the 2nd row ", 2.2, "today "}, { 3,"this is row three ", 3.3, "19-apr-91 "}, { 0, "", 0.0, ""} }; static int row_num = 0; main() { int get_row(); exec sql begin declare section; exec sql end declare section; exec sql connect dbname; exec sql drop table t; exec sql create table t (col1 integer, col2 char(20), col3 float, col4 date); exec sql copy table t (col1=integer, col2=char(20), col3=float8, col4=char(25)) from program with copyhandler = get_row; exec sql disconnect; } int get_row(byte_length, row, bytes_used) int *byte_length; char *row; int *bytes_used; { int i; char *top = row; char *fromp; if (get_data[row_num].col2[0] == '\0') { *bytes_used = 0; /* Indicate all rows copied */ return 0; } /* Copy integer data a byte at a time */ fromp = (char *)&get_data[row_num].col1; for (i = 0; i < sizeof(int); i++) { *top++ = *fromp++; } fromp = get_data[row_num].col2; /* Copy 20 bytes of char data */ for (i = 0; i < 20; i++) { *top++ = *fromp++; } fromp = (char *)&get_data[row_num].col3; /* Copy float data a byte at a time */ for (i = 0; i < sizeof(double); i++) { *top++ = *fromp++; } fromp = get_data[row_num].col4; /* Copy 25 bytes of char data */ for (i = 0; i < 25; i++) { *top++ = *fromp++; } *bytes_used = top - row; row_num++; return 0; } Example 3 /* Copy from program using variable length formats. ** Table contains: ** integer ** char(20) ** float ** date ** varchar(20) ** ** Copy uses formats: ** char(0)'/' ** varchar(0)'/' ** char(0)'/' ** char(0)'/' ** varchar(0)nl ** ** The user-defined handler copies data from a structure into the row ** buffer a whole row at a time. The fourth row contains null data in ** columns 2,3,4 and 5. */ char *get_data[] = { "1 / 19this is the 1st row / 1.1 /21-mar-1991/ 11first again\n", "2 / 19this is the 2nd row / 2.2 /today / 12second again\n", "3 / 19this is the 3rd row / 3.3 /19-apr-91 / 11third again\n", "4 / 4null /null /null / 4null\n", "" }; static int row_num = 0; main() { int get_row(); exec sql begin declare section; exec sql end declare section; exec sql connect dbname; exec sql drop table t; exec sql create table t (col1 integer, col2 char(20), col3 float, col4 date, col5 varchar(20)); /* Test with some good data */ exec sql copy table t (col1=char(0)'/' with null ('null'), col2=varchar(0)'/' with null ('null'), col3=char(0)'/' with null ('null'), col4=char(0)'/' with null ('null'), col5=varchar(0)nl with null ('null')) from program with copyhandler = get_row; exec sql disconnect; } int get_row(byte_length, row, bytes_used) int *byte_length; char *row; int *bytes_used; { int i; char ** data; char *top = row; char *fromp; if (get_data[row_num][0] == '\0') { *bytes_used = 0; /* Indicate all rows copied */ row_num = 0; return 0; } strcpy(row, get_data[row_num]); *bytes_used = strlen(row); row_num++; return 0; } Releases affected: 6.4/00(all.all) - Releases not affected: Errors: Bugs/SIRS:
© William Yuan 2000
Email William