93s523/PGEN: chgen v7

Final Project Report

91.523 Software Engineering

Computer Science Dept.

UMass-Lowell

Spring 1993

 

 

Charles Murphy

Jennifer Mick

Meier Samman

M Shamsi

Revised 2k0912 - RJL

(see footnote 1)

CONTENTS

1.0 Problem Definition 3

1.1 Introduction 3

1.2 Problems and Issues with CHGEN Version 6.2 5

1.3 Solution Strategy 6

2.0 Functional Requirements 6

2.1 pr_create 6

2.2 pr_set_int 7

2.3 pr_set_flt 7

2.4 pr_set_str 8

2.5 pr_get_int 9

2.6 pr_get_flt 10

2.7 pr_get_str 10

2.8 pr_add 11

2.9 pr_set_default 13

2.10 pr_get_default 13

2.11 Default Table Field Values 14

2.12 Example 14

3.0 Impact Analysis 21

3.1 Set/get field macros 21

3.2 pr_create routine 21

3.3 pr_parse routine 21

3.4 pr_link routine 21

3.5 pr_load (load_data) routine 21

4.0 Project Phases 23

4.1 Phase 1 - Initialization 23

4.2 Phase 2 - Prototype 23

4.3 Phase 3 - Coding 24

4.4 Phase 4 - Finalization 24

5.0 Design Specification 24

5.1 pr_create 25

5.2 Get/Set Field macros 26

5.3 Default Primary Key Assignment Macros 27

5.4 pr_add 27

5.5 load_data 30

5.6 pr_parse 30

5.7 pr_link 31

5.8 pr_dump 32

5.9 Bug Fixes 32

6.0 Testing 33

7.0 Future Considerations 34

 

1.0 Problem Definition

1.1 Introduction

The CHGEN code generation tool is used by many projects in 91.523 Software Engineering I. Therefore it is important to make CHGEN reliable, fast, easier to use, understand, and maintain. Ultimately it is desirable to have CHGEN merge to the object oriented tool GENDB. This would provide stability to CHGEN's future design

and code by hiding internal data and state representations of table objects and providing a well defined application programming interface (API).

Throughout this document the term "user" will be used to refer the application programmer and the term "current CHGEN" to refer the current CHGEN version 6.2. In addition, The term "targeted CHGEN" will be used to refer CHGEN version 7.0.

The following matrix generation example will be used for illustration and comparison of the current and targeted version 7.0:

FIG 1: SCHEMA FILE FOR

MATRIX GENERATION PROGRAM

/************************************************************************/

/* File: matrix.sch */

/* Schema file for the matrix generation program. */

/************************************************************************/

Matrix MA /* schema for matrix object */

{

MAid matrix_id 8 1 /* primary key - matrix id */

name name c16 0 /* name of matrix */

rowdim row_dim i2 0 /* number of rows */

coldim col_dim i2 0 /* number of columns */

}

Row RO /* schema for row object, related to a matrix */

{

ROid row_id 8 1 /* primary key - row id */

MAid matrix_id c8 1 /* foreign key - matrix id */

rowndx row_index i2 0 /* row number */

}

Column CO /* schema for column object, related to a matrix */

{

COid column_id c8 1 /* primary key - column id */

MAid matrix_id c8 1 /* foreign key - matrix id */

colndx column_index i2 0 /* column number */

}

Element EL /* schema for element, related to a row and column */

{

ELid element_id c8 1 /* primary key - element id */

ROid row_id c8 1 /* foreign key - row id */

COid column_id c8 1 /* foreign key - column id */

value value f4 0 /* value */

}

 

 

FIG 2: MATGEN.C - SPARSE MATRIX GENERATION PROGRAM THAT USES CHGEN VERSION 6.2

/**************************************************************************************/

/* File: matgen.c */

/* This program generates a matrix of a given dimensions. */

/* The following program uses CHGEN to generate a matrix of a given */

/* dimension. The function matgen generates ascending integer entries */

/* in the matrix, but could just as well have used random numbers to */

/* produce a small number of non-zero elements for a sparse matrix test. */

/**************************************************************************************/

#include "matrix.h"

#define MAIN

 

main()

{

char *matrix_name;

int matrix_n_rows;

int matrix_n_cols;

 

matrix_name = argv[1];

matrix_n_rows = atoi( argv[2] );

matrix_n_cols = atoi( argv[3] );

matgen("view.dat", "matrix", "MA.dat", matrix_name, matrix_n_rows, matrix_n_cols);

}

/******************************/

/* Matrix generation function */

/******************************/

matgen(viewfile, view, datafile, name, n_rows, n_cols)

char *viewfile, *view, *datafile, *name;

int n_rows, n_cols;

{

int row, col;

float elm_value = 0.0;

char MA_pkey[9];

char RO_pkey[9];

char CO_pkey[9];

char EL_pkey[9];

char buffer[1024];

pr_init( viewfile, "" );

pr_gen_pkey( view, MA, MA_pkey );

sprintf( buffer,"%s %s %d %d", MA_pkey , name, n_rows, n_cols);

pr_add( view, buffer );

for(row=1; row <= n_rows; row++)

{

pr_gen_pkey( view, RO, RO_pkey );

sprintf(buffer, "%s %s %d", RO_pkey , MAelt->MAid, row);

pr_add( view, buffer );

for(col=1; col <= n_cols; col++)

{

pr_gen_pkey( view, CO, CO_pkey );

sprintf(buffer, "%s %s %d", CO_pkey , MAelt->MAid, col);

pr_add( view, buffer );

++elm_value;

pr_gen_pkey( view, EL, EL_pkey );

sprintf( buffer, "%s %s %s %8.2f", EL_pkey ,

ROelt->ROid, COelt->COid, (float) elm_value );

pr_add( view, buffer );

}

}

pr_dump( view, datafile, 0, "w" );

pr_free();

}

 

1.2 Problems and Issues with CHGEN Version 6.2

CHGEN version 6.2 requires that the application programmer build a buffer which holds the values of table fields each time a table record is to be added to the database. For example, to add a matrix entry in the above example, the following code is executed:

pr_gen_pkey( view, EL, EL_pkey );

sprintf( buffer, "%s %s %s %8.2f", EL_pkey ,

ROelt->ROid, COelt->COid, (float) elm_value );

pr_add( view, buffer );

The function pr_add parses this input buffer and sets the corresponding table fields to the parsed values. There are reliability and performance problems with this approach illustrated in the following:

 

1. Table field values are set indirectly by the user via string buffers. Here the user builds the buffer "line" which is then parsed by pr_add to set table field values based on the order in which they appear in the buffer. Although the sprintf function is versatile, the format string can be cumbersome. The user must be vigilantly aware of the number of fields, field size, and field type of a record for each table. This approach is unreliable and can be very dangerous if the user did not build the buffer correctly. For example, when the format string, number of fields, and the field types don't correspond to the schema table fields of the table record being added.

2. This buffer building by the user and parsing by pr_add is not necessary. It has a major performance impact on program execution. The function sprintf is called to construct buffers by the user. Then the functions atof and atoi are called internally by pr_add to convert the corresponding parsed buffer strings into floats/doubles and integers.

3. The API is not clear. The user has no understanding of where table entries are created and how table fields are set. Internally, the function pr_add creates table entries and sets table fields for the user. This violates the claim that CHGEN is object-oriented.

 

1.3 Solution Strategy

Our solution dealt with the problems of CHGEN v. 6.2 described above. So, the

targeted CHGEN is intended to be faster, more reliable and easier to use and understand. The solution for these problems involved introducing a new API and code modification to CHGEN version 6.2.

For the new API, we eliminated the unreliable, slow, and complex buffer building

and provided a new set of functions (or macros) that would allow new CHGEN users to create tables and set table field values explicitly. In addition, another set of macros was provided to get table field values. The only API changes to the current CHGEN involved the number and type of argument passed to pr_add. Refer to the Functional Requirements section below for more details.

The function pr_add is used both externally by application programmers and internally by CHGEN when a call to pr_load is made to load user databases. Note, however, that when a database is loaded, input buffers are assumed to have been built correctly and so buffers need only be parsed. For this reason, code modification to CHGEN v 6.2 involved breaking down pr_add into two parts: the first part parses the input buffer, create table objects, and sets the corresponding table field values. The second part links (or adds) table records to the corresponding table link lists. Refer to the Design Specification section for more details.

The four phases of the project were:

Phase 1 - Initialization -- Analyzed the problems and provided solutions.

Phase 2 - Prototype -- Modified CHGEN-produced code and tested on matrix product problem.

Phase 3 - Coding -- Made changes to current CHGEN code and produce the targeted CHGEN.This included testing using matrix product problem, students database, state machine problem, and an error test.

Phase 4 - Finalization -- Final report, updated user documentation.

2.0 Functional Requirements

The following subsections describe the targeted CHGEN API. Refer to the Design Specification

section for more information on how these functions/macros are implemented and what their impact is on

the current version of CHGEN.

2.1 pr_create

Description:

pr_create allocates a table element and initializes it. It returns a pointer to the table element created.

Syntax:

tbl_type * pr_create(tbl_abbrv)

where tbl_abbrv is the table abbreviation defined in the user schema file.

Returns: (side effect: allocates space but does not insert field values or relationship link pointers.

pointer to table element).

Example

struct MA *MA_elt;

MA_elt = pr_create(MA);

 

2.2 pr_set_int

Description:

pr_set_int sets a schema table field of type i2 or i4 to a given value

.

Syntax:

void pr_set_int( tbl_elt, field, value)

where

tbl_elt is the table element pointer created by pr_create.

field is the field name within the table.

value is the integer value for the field.

Returns: none [-Has side effect on table row at arg1.]

Example:

struct MA *MA_elt;

MA_elt = pr_create(MA);

pr_set_int( MA_elt, rowdim, 5);

 

2.3 pr_set_flt

Description:

pr_set_flt sets a schema table field of type f4 or f8 to a given value.

Syntax:

void pr_set_flt( tbl_elt, field, value)

where

tbl_elt is the table element pointer created by pr_create.

field is the field name within the table.

value is the floating point value for the field.

Returns:

N/A [Side effect on specified row and field. ]

Example:

struct EL *EL_elt;

EL_elt = pr_create(EL);

pr_set_flt( EL_elt, value, 2.5 );

 

2.4 pr_set_str

Description:

pr_set_str sets a schema table field of type cX or tX to a given value. If the length of the value string is longer than the length of the X, then a warning message will be issued and the field value will be truncated.

Syntax:

void pr_set_str( tbl_elt, field, value)

where

tbl_elt is the table element pointer created by pr_create.

field is the field name within the table.

value is the character string value for the field.

Returns:

N/A [-Has side effect on specified row and field value.]

Error Messages:

      1. If the string length of the value being assigned is longer than X, the size of the field, the following warning message will be printed out. In this case, only the first X characters will be copied.

Warning: field <field name> in table <table abbreviation> <hex-value> is too long - truncated

Note: If the length of a field value of type cX is less than X, the string is automatically padded with spaces to align columns when the table is stored in the database via a call to pr_dump(). <hex- value> is the address of the table.

Example:

struct MA *MA_elt;

MA_elt = pr_create(MA);

pr_set_str( MA_elt, name, "MYMATRIX" );

pr_set_str( MA_elt, name, "MYMATRIX_2" );

Warning: field "name" in table MA 0x0000c440 is too long - truncated

Note that the value of field "name" will be "MYMATRIX" in this case.

 

2.5 pr_get_int

Description:

pr_get_int gets the value of a schema table field of type i2 or i4.

Syntax:

int pr_get_int( tbl_elt, field)

where

tbl_elt is the table element pointer.

field is the field name within the table.

Returns:

The integer value of the table field.

Example:

struct MA *MA_elt;

n_rows = pr_get_int( MA_elt, rowdim );

 

2.6 pr_get_flt

Description:

pr_get_flt gets the value of a schema table field of type f4 or f8.

Syntax:

float pr_get_flt( tbl_elt, field)

double pr_get_flt( tbl_elt, field)

where

tbl_elt is the table element pointer.

field is the field name within the table.

Returns:

The floating point value of the table field.

Example:

struct EL *EL_elt;

elt_value = pr_get_flt( EL_elt, value );

 

2.7 pr_get_str

Description:

pr_get_str gets the pointer of a schema table field of type cX or tX.

Syntax:

char * pr_get_str( tbl_elt, field)

where

tbl_elt is the table element pointer.

field is the field name within the table.

Returns:

The character string pointer of the table field.

Example:

struct MA *MA_elt;

matrix_name = pr_get_str( MA_elt, name );

 

2.8 pr_add

Description:

pr_add adds a table element to the user database. It generate a unique string value for the primary key field of the table element being added. If a field of type cX or tX is not assigned a value, pr_add issues a warning message and generates a string of length X of the character '?'.

Syntax:

void pr_add ( view, tbl_abbr, tbl_elt );

where

view is the schema view.

tbl_abbr is the table abbreviation as specified in the user schema file

tbl_elt is the table element pointer.

Returns:

N/A (- Side effects: (1) Assigns a new unused pkey value; (2) appends a new row object to its table container; (3) links it into all fkey-defined relationships; (4) Updates global current pointer <tbl_abbr>curr to the new row instance.)

Error Messages:

1. If the table element being added has foreign keys such that:

- The user did not assign values for the foreign keys or

- The user did not call pr_set_default(1),

the following warning message will be printed out:

Warning: foreign key <key name> is not set for table <table abbrev> element <hex-value>

In this case, the default foreign key value would be:

<parent table abbreviation>< 000...>

 

2. If the table element being added contains unassigned non-key field(s) of type cX or tX, pr_add issues the following warning message:

Warning: string field <field name> is not set for table <table abbreviation> element <hex-value>

Note: If the length of a field value of type cX is less than X, the string is automatically padded with spaces when the tables are stored in the database via a call to pr_dump().

 

Example:

struct MA *MA_elt;

struct RO *RO_elt;

MA_elt = pr_create(MA);

pr_set_default(1); /* Assume parent exists; no need to assign default fkey values. */

pr_set_str(MA_elt, name, "MYMATRIX");

pr_set_int(MA_elt, rowdim, n_rows);

pr_set_int(MA_elt, coldim, n_cols);

pr_add ( view, MA, MA_elt);

RO_elt = pr_create(RO);

pr_set_int( RO_elt, rowndx, row);

pr_add( view, RO, RO_elt);

Note:

The call to pr_set_default(1) in the above example instructs pr_add to copy the value of the primary key MAid of MA_elt (the parent of RO_elt in this case) into the foreign key MAid of RO_elt.

If pr_set_default(1) is dropped from the above example, the call to pr_add to add RO_elt will produce the following warning message:

Warning: foreign key MAid is not set for table element RO 0x0d0d0c0c

In this case the foreign key MAid for table element RO pointed to by RO_elt will have the value MA000000.

The following call to pr_set_str can be used to ensure that the default value for the foreign key value of RO_elt is set properly and that the warning message will not be produced:

pr_set_str( RO_elt, MAid, pr_get_str(MA_elt, MAid) );

 

2.9 pr_set_default

Description:

pr_set_default allows the user to specify how pr_add should treat un-assigned foreign key values. It is used to enable/disable default table foreign key assignment each time a table element is added to the database via a call to pr_add.

Syntax:

pr_set_default( flag )

where

flag is either 1 or 0.

Returns:

N/A

Example:

To enable foreign key default assignment:

pr_set_default(1);

To disable foreign key default assignment:

pr_set_default(0);

2.10 pr_get_default

Description:

pr_get_default returns the current value of the hcg_default_fkey integer flag

Purpose: to enable/disable default table foreign key assignment when table elements are added to the database via a call to pr_add.

Syntax:

int pr_get_default()

Returns:

the integer value of the default flag.

Example:

int flag;

flag = pr_get_default();

pr_set_default( 0 );

...

pr_set_default( flag );

 

2.11 Default Table Field Values

Once a table element has been created, the user can then set the table element field values and then add it to the database. If the user did not set a table field, then a default

value will be used. The following default values are assumed:

1. A value of 0 for table fields of type i2 and i4.

2. A value of 0.0 for table fields of type f4 and f8.

3. A value of "?" for table non-primary key fields of type cX or tX.

4. Primary keys are automatically generated and assigned by pr_add.

5. Foreign keys take the value of the last parent primary key value added if default foreign key assignment is enabled and the foreign key(s) are not set. Otherwise, the default value will be <parent table abbreviation><0's> as described above.

In most cases adding table elements to the database is done sequentially and follows a logical top-down order: a parent table (or object) is added before a child table (or object) can be added. For example, before a table element of type RO can be added, the parent of the RO element (a table element of type MA) should have been added first. However, some applications may not follow this order. That's why the function pr_set_default() is supported.

This is very important to understand when adding tables to CHGEN's database. It also has an impact on the default values of table foreign keys. The user specifies how pr_add should treat un-assigned table foreign keys and so it is the user's responsibility to ensure such behavior is the desired one. (For example, by procedurally searching for the pkey.)

 

2.12 Example

The following example shows how the targeted (new) CHGEN API can be used to

create table elements, set field values, and add table elements to the database.

 

FIG 3: MATGEN.C USING NEW CHGEN

/*****************************************************************************/

* matgen function modified to show how the CHGEN API can be used. Note: indents lost in conversion to HTML*/

*****************************************************************************/

#include "matgen.h"

#define MAIN

matgen(viewfile, view, datafile, name, n_rows, n_cols)

char *viewfile, *view, *datafile, *name;

int n_rows, n_cols;

{

struct MA *MA_elt;

struct RO *RO_elt;

struct CO *CO_elt;

struct EL *EL_elt;

int row, col;

float elm_value = 0.0;

pr_init( viewfile, "" );

MA_elt = pr_create(MA);

pr_set_str( MA_elt, name, name);

pr_set_int( MA_elt, rowdim, n_rows);

pr_set_int( MA_elt, coldim, n_cols);

pr_add ( view, MA, MA_elt);

for(row=1; row <= n_rows; row++)

{

RO_elt = pr_create(RO);

pr_set_int( RO_elt, rowndx, row);

pr_add( view, RO, RO_elt);

for(col=1; col <= n_cols; col++)

{

CO_elt = pr_create(CO);

pr_set_int( CO_elt, colndx, col);

pr_add( view, CO, CO_elt);

++elm_value;

EL_elt = pr_create(EL);

pr_set_flt( EL_elt, value, elm_value);

pr_add ( view, EL, EL_elt);

/*

* Note that the values for the foreign keys of table EL

* will have default values:

*

* EL_elt->ROid = RO_elt->ROid;

* EL_elt->COid = CO_elt->COid;

*

* and the default value for:

*

* RO_elt->MAid = MA_elt->MAid;

* CO_elt->MAid = MA_elt->MAid;

*

*/

}

}

pr_dump( view, datafile, 0, "w" );

pr_free();

}

2.13 Data Flow Diagrams and Structure Charts:

The following four pages contain a DFD for matgen.c (FIG 2) using CHGEN ver 6.2, the corresponding SCD, a DFD for matgen.c using targeted CHGEN ver 7.0 (FIG 3), and the corresponding SCD.

 

3.0 Impact Analysis

The code that was initially modified was generated routines after the schema file had been run through chgen. This was proof of concept. After testing and showing that the modifications were successful then the original code was revised. The modified code is not backward compatible due to the modification of the pr_add routine.

3.1 Set/get field macros

The set/get field macros are #defined in the header file, <schema>.h. The following table shows the file affected and the code that generated the file in CHGEN v. 6.2:

TABLE 1

File Affected File Generated by

header file gen_defines.c

 

3.2 pr_create routine

pr_create is placed in the file pr_load.c.

3.3 pr_parse routine

The portion of pr_add that the current CHGEN uses to parse input buffers is placed in a new function which will be called pr_parse. This function is called from load_data (called from pr_load) to parse input buffers. Once a buffer is parsed, pr_parse will create the corresponding table element and sets the corresponding table element field values based on the parsed buffers.

3.4 pr_link routine

The portion of pr_add that the current CHGEN uses to link table elements to the link list is placed in a new function which will be called pr_link. This function is called from load_data (called from pr_load) to link the table elements created by pr_parse.

3.5 pr_load (load_data) routine

pr_load calls the function load_data which in turn calls pr_add to add a new table element. The call to pr_add from load_data will be replaced by a call to pr_parse followed by a call to pr_link as described above.

For 3.2 - 3.5, table 2 summarizes the routine affected, the file that has the routine, and the chgen source file that generated the file for CHGEN version 6.2:

TABLE 2

Routine Affected File Routine in File generated by

pr_add() pr_load.c gen_pr_load.c and gen_pr_add.c

pr_load() pr_load.c gen_pr_load.c

load_data() pr_load.c gen_pr_load.c

Table 3 summarizes the functions used in CHGEN v. 6.2 by pr_add(), where the

function is defined, and the code that generated the file.

TABLE 3

Function File Function is in Generated by

find_view_idx() pr_load.c gen_pr_load.c

find_tbl_idx() pr_load.c gen_pr_load.c

extract_ver_row() pr_load.c gen_pr_load.c

alloc_element() header file gen_defines.c

mystrcpy() pr_load.c gen_pr_load.c

hcg_parse() pr_load.c gen_pr_load.c

insert_element() header file gen_defines.c

link_child_bp_m() header file gen_defines.c

link_parent_bp_m() header file gen_defines.c

Table 4 summarizes the functions used in CHGEN v.6.2 by load_data(), where the

function is defined, and the chgen code that generated the file.

TABLE 4

Function File function is in Generated by

hcg_read_next() pr_load.c gen_pr_load.c

hcg_parse() pr_load.c gen_pr_load.c

extract_ver_row() pr_load.c gen_pr_load.c

pr_add() pr_load.c gen_pr_load.c aaand gen_pr_add.c

Table 5 summarizes the functions used in CHGEN v. 6.2 by pr_load(), where the function is defined, and the code that generated the file.

TABLE 5

Function File function is in Generated by

find_view_idx() pr_load.c gen_pr_load.c

load_data() pr_load.c gen_pr_load.c

Overall, pgen modified the following eight files:

chgen_define.h

gen_defines.c

gen_load_data.c

gen_pr_add.c

gen_pr_delete.c

gen_pr_dump.c

gen_pr_free.c

gen_pr_utils.c

 

4.0 Project Phases

4.1 Phase 1 - Initialization

4.1.1 Analyzed current CHGEN v. 6.2 code

4.1.2 Listed target files to be changed

4.1.3 Started Preliminary Design Review

4.2 Phase 2 - Prototype

4.2.1 Set up development environment

4.2.1.1 Organization of project directories

The project directories were organized as follows:

usr/proj3/case/93s523/pgen has a code base directory and directories for the following users::

cmurphy lechner jmick msamman mshamsi

The baseline directory /usr/proj3/case/93s523/pgen/base has the following sub-directories:

doc executables includes lib src test tools

Sub-directory src/NEW contains the new CHGEN files. See Appendix IX.

Sub-directory test is described in section 6.0. See also Appendix XI

4.2.1.2 SCCS directories

4.2.2 Prototype - made changes to code produced by CHGEN v. 6.2 and tested results. The schema used was that of the sparse matrix product assignment (matgen). This involved:

4.2.2.1 Added set/get field macros into matgen.h

4.2.2.2 Added pr_create macro to matgen.h

4.2.3.3 Modified pr_add in pr_load.c

4.2.3 Tested changes and reviewed with Dr. Lechner

4.2.4 Updated PDR

 

4.3 Phase 3 - Coding

4.3.1 Coded changes to corresponding files

4.3.2 Tested changes on matgen, students and stm schema. Used students database to implement error tests.

4.3.3 Detail design specifications, implementation issues

4.3.4 Reviewed with Dr. Lechner

4.4 Phase 4 - Finalization

4.4.1 Updated current CHGEN user documentation

4.4.2 Final Document

 

5.0 Design Specification

The following table summarizes all the new or modified functions, macros,

and global variables in the new CHGEN version 7.0.

TABLE 6: New or modified genv7 macros and functions

Name Kind Generated by File CHGEN Output File

--------------- -------- ---------------- -------------------

hcg_ptr data type gen_defines.c <schema>.h

pr_create macro gen_defines.c <schema>.h

pr_gen_create function gen_pr_add.c pr_load.c

pr_set_int macro gen_defines.c <schema>.h

pr_set_flt macro gen_defines.c <schema>.h

pr_set_str macro gen_defines.c <schema>.h

pr_get_int macro gen_defines.c <schema>.h

pr_get_flt macro gen_defines.c <schema>.h

pr_get_str macro gen_defines.c <schema>.h

* pr_add macro gen_defines.c <schema>.h

pr_gen_key macro gen_pr_add.c pr_load.c

* pr_gen_pkey macro gen_pr_add.c pr_load.c

pr_check_str macro gen_pr_add.c pr_load.c

pr_check_fkey macro gen_pr_add.c pr_load.c

gen_pr_add function gen_pr_add.c pr_load.c

hcg_default_fkey global_var gen_defines.c <schema>.h

pr_set_default macro gen_defines.c <schema>.h

pr_get_default macro gen_defines.c <schema>.h

pr_parse function gen_load.c pr_load.c

pr_link function gen_load.c pr_load.c

* load_data function gen_load.c pr_load.c

* hcg_within_load global_var gen_defines.c <schema>.h

* pr_dump function gen_pr_dump.c pr_dump.c

** alloc_element macro gen_pr_add.c pr_load.c

** insert_element macro gen_pr_add.c pr_load.c

** link_child_1 macro gen_pr_add.c pr_load.c

** link_child_bp_m macro gen_pr_add.c pr_load.c

** link_child_nobp_m macro gen_pr_add.c pr_load.c

** link_parent_1 macro gen_pr_add.c pr_load.c

** link_parent_bp_m macro gen_pr_add.c pr_load.c

** link_parent_nobp_m macro gen_pr_add.c pr_load.c

** unlink_child_1 macro gen_pr_delete.c pr_delete.c

** unlink_child_bp_m macro gen_pr_delete.c pr_delete.c

** unlink_child_nobp_m macro gen_pr_delete.c pr_delete.c

** unlink_parent_1 macro gen_pr_delete.c pr_delete.c

** unlink_parent_bp_m macro gen_pr_delete.c pr_delete.c

** unlink_child_nobp_m macro gen_pr_delete.c pr_delete.c

** free_table macro gen_pr_free.c pr_free.c

-------------------------------------------------------------------------------

* CHGEN version 6.2 function/macro which is revised in CHGEN version 7.0.

** CHGEN version 6.2 private macro moved from <schema>.h file to corresponding file on column 4 above in version 7.0.

5.1 pr_create

A new data type was introduced in CHGEN version 7.0. This data type was used to cast table objects into generic pointers and vice versa. This data type was called "hcg_ptr" and was added to <schema>.h file and was defined as follows:

#ifdef __STDC__

typedef void * hcg_ptr;

#else

typedef char * hcg_ptr;

#endif

Note that in ANSI "C" the macro "__STDC__" is pre-defined by the ANSI compilers. Also note that the above type definition is independent from the "-ansi" flag of CHGEN. Chgen is designed to support both 32 and 64-bit machines, whose addresses are the size of unsigned int or unsigned long int, respectively.

pr_create was defined as a macro in <schema>.h file as follows:

#define pr_create(tbl_abbr) \

(struct tbl_abbr *) pr_gen_create( sizeof( struct tbl_abbr) )

pr_gen_create is a function that allocates and initializes memory for the table element pointer as shown below.

hcg_ptr pr_gen_create ( size )

int size;

{

hcg_ptr ptr = (hcg_ptr) malloc(size);

bzero(ptr, size); /* or memset(ptr, 0, size); */

return ptr;

}

The purpose of the pr_create() macro is to pass the size of the table object to the pr_gen_create() and to cast the returned value to the table object data type.

 

5.2 Get/Set Field macros

The following macros were defined in <schema>.h.]

#define pr_set_int(tbl,field,value) (tbl)->field = (value)

#define pr_set_flt(tbl,field,value) (tbl)->field = (value)

#define pr_set_str(tbl,field,value) \

do { \

if ( strlen(value) >= sizeof((tbl)->field) ) \

fprintf(stderr, "Warning: field %s in table %s is too long - truncated\n

", "field", "tbl");\

strncpy((tbl)->field, (value)); \

} while(0)

#define pr_get_int(tbl,field) (tbl)->field

#define pr_get_fp(tbl,field) (tbl)->field

#define pr_get_str(tbl,field) (tbl)->field

 Note that the do {} while(0) is a technique used by many programmers. The purpose of this technique is to reduce multiple statements into a single statement. This technique protects the user of this macro from syntactic and possibly run-time errors.

 

5.3 Default Primary Key Assignment Macros

A new global variable "hcg_default_fkey" was added to <schema>.h. This flag variable holds the current value of the default foreign key assignment mode. The flag was initialized statically to 0.

#ifdef MAIN

int hcg_default_fkey = 0;

#else

extern int hcg_default_fkey;

#endif

The following macros were used to get/set the value of the default foreign key assignment flag:

#define pr_set_default(val) hcg_default_fkey = (val)

#define pr_get_default(val) hcg_default_fkey

 

 

5.4 pr_add

pr_add was defined as a macro in file <schema>.h:

#ifdef MAIN

#define pr_add(view,tbl_abbr,tbl_ptr) gen_pr_add(view, "tbl_abbr", tbl_ptr)

#endif

This macro is used only to add double quotes to the table abbreviation rather than asking the user to pass a double-quoted argument. It is a matter of consistency and simplicity rather than a requirement.

 

Before describing how gen_pr_add will add new elements, the following macros will be described and then their use by gen_pr_add will be shown.

- pr_check_str

- pr_check_fkey

- pr_gen_key

- pr_gen_pkey

Two macros were used to generate a new primary key. Note that the macro pr_gen_pkey is similar to CHGEN version 6.2. The only difference is that pr_gen_pkey now calls pr_gen_key. The macro pr_gen_key is used to set the default value of un-assigned foreign keys as shown below in macro pr_check_fkey. Note that the macro pr_gen_pkey has been moved from <schema>.h file to pr_load.c in the new CHGEN version 7.0. This is because pr_gen_pkey will be called by pr_add only; it is no longer public and need not be defined in <schema>.h.

#define pr_gen_key(key,tbl_abbrev,version_id,entry_id) \

sprintf( key, "%s%02d%04d", (tbl_abbrev), (version_id), (entry_id) )

#define pr_gen_pkey(viewname,tbl,pkey) \

if (find_view_idx(viewname)) \

pr_gen_key(pkey,hcg_table_seq_list[tbl/**/_idx].ttabbrev, \

(char) hcg_view_list.view_list[hcg_view_idx].version_list[tbl/**/_idx], \

hcg_ts_list[tbl/**/_idx].\

ts_list[hcg_view_list.view_list[hcg_view_idx].\

version_list[tbl/**/_idx]].maxrow+1)

The following macro checks that the user had already set table foreign key fields. If such fields are not set and the user did not set the default foreign key assignment flag via pr_set_default(1), a warning message will be issued and the field value will be set to <table_abbr>000000.

#define pr_check_fkey(ctbl_abbr,ctbl_ptr,ckey,ptbl_abbr,pkey,ptbl_ptr) \

if ( (ctbl_ptr)->ckey[0] == '\0' )\

{\

if (!hcg_default_fkey) \

{\

printf("Warning: foreign key %s is not set for table %s element 0x%8.8x\n", \

"ckey", "ctbl_abbr", ctbl_ptr );\

pr_gen_key( (ctbl_ptr)->ckey,"ptbl_abbr", 0, 0);\

}\

else if ( (ptbl_ptr) && (ptbl_ptr)->pkey[0] != '\0' ) \

strcpy( (ctbl_ptr)->ckey, (ptbl_ptr)->pkey );\

else\

pr_gen_key( (ctbl_ptr)->ckey, "ptbl_abbr", 0, 0);\

}

 

The following macro is used set the default value of non-key fields of type cX or tX.

#define pr_check_str(tbl_abbr,tbl_ptr,fld,length) \

if ( (tbl_ptr)->fld[0] == '\0' ) \

{\

int i = sizeof((tbl_ptr)->fld) - 2;\

printf("Warning: string field %s is not set for table %s element 0x%8.8x\n", \

"fld", "tbl_abbr", tbl_ptr);\

for((tbl_ptr)->fld[i] = '\0'; i>=0; (tbl_ptr)->fld[i--] = '?');\

}

 

The function gen_pr_add will first find the view index "hcg_view_idx" and the table index "hcg_view_idx" based on its input argument. This is done exactly the same way the current CHGEN version 6.2 pr_add() computes these values. Then, the table encoding is computed. Based on the table encoding, gen_pr_add() first casts its input argument tbl_ptr, the generic pointer returned by pr_create(). Then, it generate a new primary key for the table element using pr_gen_pkey. After that, it calls one or more of the above described macros to check and set or unset table element fields. Finally, it inserts the table element into the corresponding table link list by calling pr_link().

Example: ( Assuming that hcg_tbl_idx has been computed )

tbl_encoding = encoding( hcg_table_seq_list[hcg_tbl_idx].ttabbrev );

switch( tbl_encoding )

{

case 312 : /* encoding of MA */

MAelt = (struct MA *) tbl_ptr;

pr_gen_pkey(viewname,MA,MAelt->MAid);

pr_check_str (MA,MAelt,name,16);

pr_link(tbl_encoding);

break;

case 456 : /* encoding of RO */

ROelt = (struct RO *) tbl_ptr;

pr_gen_pkey(viewname,RO,ROelt->ROid);

pr_check_fkey(RO,ROelt,MAid,MA,MAid,MAelt);

pr_link(tbl_encoding);

break;

...

}

 

 

 

 

 

5.5 load_data

This function was modified in the new CHGEN to replace the call

to pr_add() by a call to pr_parse() followed by a call to pr_link().

The following code:

hcg_within_load = idx;

pr_add(viewname,hcg_buffer);

hcg_within_load = -1;

was replaced by:

tbl_encoding = encoding( hcg_table_seq_list[hcg_tbl_idx].ttabbrev);

pr_parse(viewname, hcg_buffer, tbl_encoding, idx);

pr_link( tbl_encoding );

Note that the global variable hcg_within_load is no longer needed since

pr_parse will not be called by the user.

 

5.6 pr_parse

This function takes four arguments as follows:

pr_parse(viewname, hcg_buffer, tbl_encoding, idx)

where:

viewname is the view name parsed by load_data.

hcg_buffer is the buffer currently being parsed.

tbl_encoding is the integer encoding of a table abbreviation

idx is the byte offset computed by load_data.

 

Based on the table encoding, pr_parse will first call pr_create() to create new memory for the table element. It then sets the table element field values as is currently being done by the function pr_add() in CHGEN version 6.2.

 

Example:

switch( tbl_encoding )

{

case 312 : /* encoding of MA */

MAelt = pr_create(MA);

mystrcpy(MAelt->MAid,hcg_t,8,0);

hcg_parse(buffer,hcg_t,&idx);

mystrcpy(MAelt->name,hcg_t,16,0);

hcg_parse(buffer,hcg_t,&idx);

pr_set_int(MAelt,rowdim,atoi(hcg_t));

hcg_parse(buffer,hcg_t,&idx);

pr_set_int(MAelt,coldim,atoi(hcg_t));

break;

...

}

 

5.7 pr_link

This function takes one argument as follows:

pr_link( tbl_encoding )

where:

tbl_encoding is the integer encoding of the table abbreviation

to be linked to the table corresponding link list.

Based on the table encoding, pr_link() calls insert_element() and link_child*() and link_parent*(), as in CHGEN version 6.2 pr_add().

 

Example:

switch( tbl_encoding )

{

...

case 456 : /* encoding of RO */

insert_element(RO,ROid);

link_child_bp_m(EL,RO,ROid,ROid,ROid,ELid);

link_parent_bp_m(RO,MA,MAid,MAid,MAid,ROid);

break;

...

}

 

5.8 pr_dump

In CHGEN version 6.2 function pr_dump() writes table records into the target data file unformatted. However, this is because table fields or type cX or tX are assumed to have been formatted previously via calls to pr_add(). Since the pr_set_str macro is used to set such fields, the new CHGEN version 7.0 writes table records formatted. That is, fields of type cX or tX are padded with spaces in pr_dump() rather than in pr_set_str macro.

 

5.9 Bug Fixes

1. included stdio.h in <schema>.h.

In CHGEN version 6.2, the user had to include stdio.h in order to be able to compile his/her application programs. This is now fixed in version

The following bugs were fixed in CHGEN version 7.0:

2. included math.h in pr_load.c.

The function atof() was used without including math.h. This caused the returned value by atof() to be casted to "int"

by the compiler. This bug is now fixed in version 7.0.

3. The function pr_parse makes a distinction between types f4 and f8 and casts the returned value of atof() if the type is i4. CHGEN version 6.2 does not do that which could produce invalid results.

4. The macro PrintCheck fails on SYSV machines. CHGEN version 6.2 defines PrintCheck as follows:

#define PrintCheck(x) \

if (x) \

{ printf(\"Error in outputting file. Exiting\\n\"); exit(1);}\

where x is a function call.

The following definition of PrintCheck is used in version 7.0:

#define PrintCheck(x)

if ((x) == -1) \

{ printf(\"Error in outputing file. Exiting\\n\"); exit(1);}\

That is because the returned value of x (i.e. printf) is not 0 on SYSV.

5. Included stdlib.h in file gen_pr_utils.c only when ANSI-C compilers are used.

6. CHGEN should exit with status 0 upon successful completion.

Currently CHGEN version 6.2 exits with status 1. This is now fixed in version 7.0.

 

6.0 Testing

Testing the new CHGEN involved the following:

1. Verified basic functionality. Testing table element creation, addition to the database, and database loading was tested by using the following functions:

- pr_create()

- pr_add()

- pr_set_XXX()

- pr_get_XXX()

- pr_load()

- pr_parse()

- pr_link()

2. Verified data integrity. This involved testing data insertion and deletion from the database and verifying the results.

3. Verified unit functionality. Verify that each function/macro behaved as expected and as documented. This involved verifying that the correct error/warning messages are produced in case of errors.

Four tests were used for these purposes. They were:

1. stm -- a test that produces a state machine transition tables.

2. matgen -- a matrix generation program.

3. students -- student roster database manipulation program.

4. err -- verified new CHGEN version 7.0 error messages.

This program is based on the students roster program.

The first three tests have two versions. One version using CHGEN version 6.2 and the other using CHGEN version 7.0. These tests are located under:

/usr/proj3/case/93s523/pgen/base/test/

ver_6.2/matgen

ver_6.2/students

ver_6.2/stm

ver_7.0/matgen

ver_7.0/students

ver_7.0/stm

ver_7.0/err

A shell script called test_verify was used to build and run both versions of these tests and verify the results. The script basically runs the diff command on the output produced from ver 6.2 and ver 7.0. If there is a difference in the 2 outputs then the script will return "test failed" else it returns "test passed". The results of this test were that the new version did not produce output that was different from the old version. Thus the tests passed.

7.0 Future Considerations

1. Too many global variables are declared implicitly by CHGEN. These global variables are used as temporary variables by CHGEN to add, delete or loop through tables. Once our implementation of the new CHGEN is completed, many of theses global variables can be eliminated.

2. Many long macros can be replaced by function calls. This will reduce the size of CHGEN produced code.

3. Macros used by CHGEN ver 6.2 are not safe or robust. For example, not all use the do {} while(0) technique described in 5.2.

4. CHGEN produced schema header file defines structures and macros that are "flagged" based on the definition of the macro MAIN. Also, the produced header file defines macros that are used by CHGEN internally. As a result, the produced header file tends to be lengthy and difficult to read. It would be a good idea to have CHGEN produce two header files rather than one. One header file, called <schema>.h should define only table objects and macros that are visible to the user and would be included by user application "C" files. The other one, called <schema>_int.h defines all other macros and objects used internally by CHGEN-generated code and would be included by the produced "C" (i.e. pr_load.c, pr_dump.c ...etc.) The advantages of this approach are:

I. Hide/isolate CHGEN internal macro and table structuredefinitions from the user.

II. Would truly confirm to object-oriented rules. That is, the external header file would include the public/published macros.

III. Make the schema header file(s) more readable by users.

IV. Eliminate the need to define MAIN in user applications.

Note that the internal header file <schema>_int.h can include the "external" header file <schema>.h.

5. The function clear_mem() is not needed. Either memset() or bzero() can be used depending on the system. UNIX BSD systems (and some SYSV systems) support bzero() while SYSV systems and VMS support memset(). Both memset() and bzero() are faster than clear_mem() and so clear_mem() should be replace by bzero(). To do so, add a compiler flag BSD based on the system for which CHGEN generates code and add the following to <schema>.h:

#ifndef BSD

#define bzero(ptr,size) memset(ptr,0,size)

#endif

 

 

APPENDIX - Source Listings

 

[These appendices are too lengthy to be reproduced here; they are available in the project directory. - RJL 2k0912]

I. chgen_define.h

II. gen_defines.c

III. gen_load_data.c

IV. gen_pr_add.c

V. gen_pr_delete.c

VI. gen_pr_dump.c

VII. gen_pr_free.c

VIII. gen_pr_utils.c

IX. Directory listing of /usr/proj3/case/93s523/pgen/base/src/NEW.

The *.diff files contain the output of the diff command used on the old and new code,

i.e. diff OLD/gen_defines.c NEW/gen_defines.c.

X. /usr/proj3/case/93s523/pgen/base/src/NEW/Makefile

XI. Directory listing of /usr/proj3/case/93s523/pgen/base/test