]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * $Header$ | |
3 | */ | |
4 | ||
5 | #ifndef lint | |
6 | static char *rcsid_gdb_struct_c = "$Header$"; | |
7 | #endif | |
8 | ||
9 | ||
10 | /************************************************************************/ | |
11 | /* | |
12 | /* gdb_struct.c | |
13 | /* | |
14 | /* GDB - Structured Data Maintenance Routines | |
15 | /* | |
16 | /* Author: Noah Mendelsohn | |
17 | /* Copyright: 1986 MIT Project Athena | |
18 | /* For copying and distribution information, please see | |
19 | /* the file <mit-copyright.h>. | |
20 | /* | |
21 | /* These routines implement the following layers of the | |
22 | /* Client Library Specification of the GDB system: | |
23 | /* | |
24 | /* Layer Function | |
25 | /* ----- -------- | |
26 | /* 2 Structured Data Management at a | |
27 | /* Single Site | |
28 | /* | |
29 | /* 4 Memory Management | |
30 | /* | |
31 | /* 5 String Management | |
32 | /* | |
33 | /* Some of the routines specified are actually implemented as | |
34 | /* macros defined in gdb.h. | |
35 | /* | |
36 | /************************************************************************/ | |
37 | ||
38 | #include <mit-copyright.h> | |
39 | #include <stdio.h> | |
40 | #include <stdlib.h> | |
41 | #include <string.h> | |
42 | #include "gdb.h" | |
43 | ||
44 | \f | |
45 | /************************************************************************/ | |
46 | /* | |
47 | /* MEMORY MANAGEMENT | |
48 | /* | |
49 | /* In anticipation of the day when we may want to do something | |
50 | /* fancy with memory management, all of the gdb routines which | |
51 | /* require dynamic allocation of memory call the routines named | |
52 | /* db_alloc and db_free. For the moment, these are implemented | |
53 | /* as calls to malloc and free. | |
54 | /* | |
55 | /************************************************************************/ | |
56 | ||
57 | /*----------------------------------------------------------*/ | |
58 | /* | |
59 | /* gdb_am | |
60 | /* | |
61 | /* Allocate memory for use by gdb. Current implementation | |
62 | /* just does a malloc. | |
63 | /* | |
64 | /*----------------------------------------------------------*/ | |
65 | ||
66 | char * | |
67 | gdb_am(bytes) | |
68 | int bytes; | |
69 | { | |
70 | return(malloc((unsigned)bytes)); | |
71 | } | |
72 | ||
73 | /*----------------------------------------------------------*/ | |
74 | /* | |
75 | /* gdb_fm | |
76 | /* | |
77 | /* Return allocated memory. Note: the current | |
78 | /* implementation ignores the byte count supplied, | |
79 | /* but future implementations may require that it | |
80 | /* be correct. | |
81 | /* | |
82 | /*----------------------------------------------------------*/ | |
83 | ||
84 | /*ARGSUSED*/ | |
85 | int | |
86 | gdb_fm(ptr, bytes) | |
87 | char *ptr; | |
88 | int bytes; | |
89 | { | |
90 | free(ptr); | |
91 | return; | |
92 | } | |
93 | \f | |
94 | /************************************************************************/ | |
95 | /* | |
96 | /* STRING MANAGEMENT | |
97 | /* | |
98 | /* To allow dynamic manipulation of strings in gdb without | |
99 | /* excessive memory re-allocation, we define a string as a | |
100 | /* counted byte space. Though this space will frequently be used | |
101 | /* to store a standard null terminated string, that is not | |
102 | /* required. | |
103 | /* | |
104 | /* Current representation for a string is a pointer followed by | |
105 | /* an integer length. A null pointer indicates a null string, in | |
106 | /* which case the length is arbitrary. Any other pointer is to | |
107 | /* memory which was allocated by db_alloc in which must be free'd | |
108 | /* eventually with db_free. | |
109 | /* | |
110 | /************************************************************************/ | |
111 | ||
112 | /*----------------------------------------------------------*/ | |
113 | /* | |
114 | /* string_alloc (string_alloc) | |
115 | /* | |
116 | /* Fills in supplied string descriptor and returns | |
117 | /* pointer to the newly allocated data. | |
118 | /* | |
119 | /*----------------------------------------------------------*/ | |
120 | ||
121 | char * | |
122 | string_alloc(stringp, bytes) | |
123 | STRING *stringp; /* pointer to string */ | |
124 | /* descriptor to be */ | |
125 | /* filled in */ | |
126 | int bytes; /* number of bytes to alloc */ | |
127 | { | |
128 | GDB_INIT_CHECK | |
129 | ||
130 | MAX_STRING_SIZE(*stringp) = bytes; /* put length in returned */ | |
131 | /* string descriptor-- */ | |
132 | /* will be irrelavent if */ | |
133 | /* alloc fails */ | |
134 | ||
135 | STRING_DATA(*stringp) = db_alloc(bytes); /* try to get the data */ | |
136 | return (STRING_DATA(*stringp)); /* return ptr to new string */ | |
137 | /* if any */ | |
138 | } | |
139 | ||
140 | /*----------------------------------------------------------*/ | |
141 | /* | |
142 | /* string_free (string_free) | |
143 | /* | |
144 | /* Releases the data space for a gdb string. Must have | |
145 | /* been allocated with string_alloc. Remember to pass | |
146 | /* in the address of the string descriptor, not the | |
147 | /* descriptor itself! | |
148 | /* | |
149 | /*----------------------------------------------------------*/ | |
150 | int | |
151 | string_free(stringp) | |
152 | STRING *stringp; | |
153 | { | |
154 | GDB_INIT_CHECK | |
155 | ||
156 | if (stringp->ptr == NULL) | |
157 | return; | |
158 | db_free(stringp->ptr, stringp->length); | |
159 | stringp->ptr = NULL; | |
160 | stringp->length = 0; | |
161 | return; | |
162 | } | |
163 | \f | |
164 | /************************************************************************/ | |
165 | /* | |
166 | /* STRUCTURED DATA MANAGEMENT AT A SINGLE SITE | |
167 | /* | |
168 | /* These routines provide the abstraction of typed, structured | |
169 | /* data at a single site. Tuples are collections of typed fields, | |
170 | /* and they are each described by a tuple descriptor. Relations | |
171 | /* are circularly linked lists of tuples. For completeness, a | |
172 | /* relation also carries a tuple descriptor, which should match | |
173 | /* the descriptor for each of its constituent tuples. This allows | |
174 | /* a null relation to be typed. | |
175 | /* | |
176 | /* Some of the facilities of structured data management are | |
177 | /* defined as macros in gdb.h. In many cases, the routines | |
178 | /* declared below are known by defines of more descriptive | |
179 | /* names, also in gdb.h. | |
180 | /* | |
181 | /************************************************************************/ | |
182 | /************************************************************************/ | |
183 | /* | |
184 | /* TUPLE_DESCRIPTOR MANAGEMENT | |
185 | /* | |
186 | /************************************************************************/ | |
187 | ||
188 | ||
189 | /*----------------------------------------------------------*/ | |
190 | /* | |
191 | /* create_tuple_descriptor (create_tuple_descriptor) | |
192 | /* | |
193 | /* Allocates space for a tuple descriptor and fills | |
194 | /* it in. Gives up if space is not available. | |
195 | /* Should be passed a list of integer coded types | |
196 | /* and a list of string field names. | |
197 | /* | |
198 | /* Tuple descriptors are reference counted, and they are | |
199 | /* not really deleted until the reference count goes | |
200 | /* to zero. It is presumed that all callers use the | |
201 | /* create and delete routines, or otherwise maintain | |
202 | /* the reference count appropriately. | |
203 | /* | |
204 | /*----------------------------------------------------------*/ | |
205 | ||
206 | ||
207 | TUPLE_DESCRIPTOR | |
208 | create_tuple_descriptor(number_of_fields, name_list, type_list) | |
209 | char *name_list[]; | |
210 | FIELD_TYPE type_list[]; | |
211 | int number_of_fields; | |
212 | { | |
213 | register TUPLE_DESCRIPTOR tpd; /* pointer to new descriptor */ | |
214 | register int i; | |
215 | register int data_len; /* length of the actual */ | |
216 | int field_len; /* length of current field */ | |
217 | int align; /* code describing alignment */ | |
218 | /* requirement for this field*/ | |
219 | /* (4 for fullword, 1 for */ | |
220 | /* char, etc.) */ | |
221 | int next_offset; /* byte offset to next field */ | |
222 | int descriptor_length; /* length of the part of */ | |
223 | /* the allocated storage */ | |
224 | /* actually used for the */ | |
225 | /* descriptor.*/ | |
226 | int str_len; /* we also have to retain */ | |
227 | /* the string names for the */ | |
228 | /* fields. These are stored */ | |
229 | /* immediately off the end */ | |
230 | /* of the descriptor, and */ | |
231 | /* all are allocated */ | |
232 | /* together. This is the */ | |
233 | /* length of the string data */ | |
234 | char *next_name; /* place to put the next */ | |
235 | /* copied fieldname*/ | |
236 | ||
237 | ||
238 | GDB_INIT_CHECK | |
239 | ||
240 | /* | |
241 | * Calculate size and allocate descriptor | |
242 | */ | |
243 | ||
244 | descriptor_length = sizeof(struct tupl_desc) + | |
245 | (number_of_fields-1) * sizeof(struct tupld_var); | |
246 | ||
247 | str_len = 0; | |
248 | ||
249 | for (i=0; i<number_of_fields; i++) | |
250 | str_len += strlen(name_list[i]) +1; | |
251 | ||
252 | tpd = (TUPLE_DESCRIPTOR)db_alloc(descriptor_length+str_len); | |
253 | /* try to allocate it */ | |
254 | ||
255 | if (tpd == NULL) | |
256 | GDB_GIVEUP("create_tuple_descriptor (gbd_ctd) could not get enough memory.\n") | |
257 | ||
258 | /* | |
259 | * Fill in the descriptor fields: | |
260 | * | |
261 | * Each field is aligned according to its own alignment code. | |
262 | * Going in to the top of the for loop, next_offset is set to | |
263 | * the offset of the first possible byte for storing the next field. | |
264 | * During the loop, that number is rounded up, if necessary, to | |
265 | * achieve the alignment actually required for the field. Finally, | |
266 | * the length of the new field is added, which yields the first | |
267 | * possible byte of any field to follow. | |
268 | */ | |
269 | ||
270 | tpd->id = GDB_DESC_ID; | |
271 | ||
272 | tpd->ref_count = 1; /* whoever asked for creation*/ | |
273 | /* is expected to delete*/ | |
274 | ||
275 | tpd->field_count = number_of_fields; | |
276 | tpd->str_len = str_len; | |
277 | ||
278 | data_len = sizeof(struct tuple_dat) - 1; /* tuple_dat includes the */ | |
279 | /* first byte of data */ | |
280 | next_offset = 0; | |
281 | next_name = ((char *)tpd) + descriptor_length; | |
282 | /* place to put first */ | |
283 | /* field name*/ | |
284 | for (i=0; i<number_of_fields; i++) { | |
285 | /* | |
286 | * Calculate lengths and alignments for the field data. | |
287 | */ | |
288 | field_len = INT_PROPERTY(type_list[i],LENGTH_PROPERTY); | |
289 | align = INT_PROPERTY(type_list[i],ALIGNMENT_PROPERTY); | |
290 | /* | |
291 | * Copy the string field name into the newly allocated | |
292 | * space just after the descriptor itself. | |
293 | */ | |
294 | tpd->var[i].name = strcpy(next_name, name_list[i]); | |
295 | next_name += strlen(next_name) + 1; | |
296 | /* | |
297 | * Put in the type and the length for the field data | |
298 | */ | |
299 | tpd->var[i].type = type_list[i]; | |
300 | tpd->var[i].length = field_len; | |
301 | /* | |
302 | * Now store the actual offset of this field, and | |
303 | * compute the first byte address we could conceivably | |
304 | * use for the next field. | |
305 | */ | |
306 | next_offset = GDB_ROUNDUP(next_offset, align); | |
307 | tpd->var[i].offset = next_offset; | |
308 | next_offset += field_len; | |
309 | } | |
310 | ||
311 | data_len += next_offset; | |
312 | tpd->data_len = data_len; | |
313 | ||
314 | return tpd; /* return the new descriptor */ | |
315 | } | |
316 | ||
317 | /*----------------------------------------------------------*/ | |
318 | /* | |
319 | /* delete_tuple_descriptor (delete_tuple_descriptor) | |
320 | /* | |
321 | /* Return the space for a tuple descriptor | |
322 | /* | |
323 | /*----------------------------------------------------------*/ | |
324 | ||
325 | int | |
326 | delete_tuple_descriptor(t) | |
327 | TUPLE_DESCRIPTOR t; | |
328 | { | |
329 | int descriptor_length; | |
330 | register int ref_count; /* buffer the reference */ | |
331 | /* count here */ | |
332 | ||
333 | if (t == NULL) | |
334 | return ; | |
335 | ||
336 | GDB_CHECK_TPD(t, "delete_tuple_descriptor") | |
337 | ||
338 | ||
339 | /* | |
340 | * Decrement the reference count. If it's not zero, then just | |
341 | * return. | |
342 | */ | |
343 | if ((ref_count = --(t->ref_count)) >0) | |
344 | return ; | |
345 | if (ref_count <0) | |
346 | GDB_GIVEUP("Tuple descriptor reference count is less than zero") | |
347 | /* | |
348 | * Current representation is to allocate the space for the string | |
349 | * right off the end of the descriptor itself. We therefore have | |
350 | * to add their length into the amount we free. | |
351 | */ | |
352 | descriptor_length = gdb_descriptor_length(t->field_count); | |
353 | db_free((char *)t, descriptor_length+t->str_len); | |
354 | ||
355 | return ; | |
356 | } | |
357 | ||
358 | /*----------------------------------------------------------*/ | |
359 | /* | |
360 | /* field_index (field_index) | |
361 | /* | |
362 | /*----------------------------------------------------------*/ | |
363 | ||
364 | int | |
365 | field_index(tuple_descriptor, field_name) | |
366 | TUPLE_DESCRIPTOR tuple_descriptor; | |
367 | char *field_name; | |
368 | { | |
369 | register int i; | |
370 | register int n; | |
371 | register TUPLE_DESCRIPTOR tpd = tuple_descriptor; | |
372 | ||
373 | /* | |
374 | * Make sure supplied descriptor is valid | |
375 | */ | |
376 | if (tpd == NULL) | |
377 | GDB_GIVEUP("null tuple descriptor passed to field_index function") | |
378 | GDB_CHECK_TPD(tpd, "field_index") | |
379 | ||
380 | n = tpd -> field_count; | |
381 | ||
382 | /* | |
383 | * Loop through each field in descriptor, return index if match | |
384 | */ | |
385 | ||
386 | for(i=0; i<n; i++) | |
387 | if (strcmp(field_name, tpd->var[i].name) == 0) | |
388 | return i; | |
389 | /* | |
390 | * No match, return -1 | |
391 | */ | |
392 | return (-1); | |
393 | } | |
394 | ||
395 | \f | |
396 | /************************************************************************/ | |
397 | /* | |
398 | /* TUPLE MANAGEMENT | |
399 | /* | |
400 | /************************************************************************/ | |
401 | ||
402 | /*----------------------------------------------------------*/ | |
403 | /* | |
404 | /* create_tuple (create_tuple) | |
405 | /* | |
406 | /* Allocate space for a new tuple, given its | |
407 | /* descriptor. Giveup if out of memory. | |
408 | /* | |
409 | /*----------------------------------------------------------*/ | |
410 | ||
411 | TUPLE | |
412 | create_tuple(descriptor) | |
413 | TUPLE_DESCRIPTOR descriptor; | |
414 | { | |
415 | register TUPLE t; | |
416 | ||
417 | GDB_CHECK_TPD(descriptor, "create_tuple") | |
418 | ||
419 | t = (TUPLE)db_alloc(descriptor -> data_len); | |
420 | ||
421 | if (t == NULL) | |
422 | GDB_GIVEUP("create_tuple (create_tuple) could not allocate enough memory.\n") | |
423 | ||
424 | t->id = GDB_TUP_ID; | |
425 | ||
426 | t->desc = descriptor; /* fill in descriptor */ | |
427 | /* pointer in new tuple */ | |
428 | REFERENCE_TUPLE_DESCRIPTOR(descriptor); /* bump the reference count */ | |
429 | #ifdef GDB_CHECK | |
430 | /* | |
431 | * Only for the sake of keeping things clean, null out the pointers. | |
432 | * Wastes time, but helps debugging. | |
433 | */ | |
434 | t->next = t->prev = NULL; | |
435 | #endif | |
436 | ||
437 | return t; | |
438 | } | |
439 | ||
440 | /*----------------------------------------------------------*/ | |
441 | /* | |
442 | /* delete_tuple (delete_tuple) | |
443 | /* | |
444 | /* Release the data space occupied by a tuple. | |
445 | /* | |
446 | /*----------------------------------------------------------*/ | |
447 | ||
448 | int | |
449 | delete_tuple(t) | |
450 | TUPLE t; | |
451 | { | |
452 | register TUPLE_DESCRIPTOR tpd; | |
453 | if (t==NULL) | |
454 | GDB_GIVEUP("Delete_tuple called with null tuple") | |
455 | GDB_CHECK_TUP(t, "delete_tuple") | |
456 | tpd = t->desc; | |
457 | db_free((char *)t, t->desc->data_len); | |
458 | delete_tuple_descriptor(tpd); /* does a reference counted */ | |
459 | /* delete*/ | |
460 | } | |
461 | ||
462 | /*----------------------------------------------------------*/ | |
463 | /* | |
464 | /* initialize_tuple (initialize_tuple) | |
465 | /* | |
466 | /* Set each field in tuple to its null value. | |
467 | /* | |
468 | /*----------------------------------------------------------*/ | |
469 | ||
470 | int | |
471 | initialize_tuple(t) | |
472 | TUPLE t; | |
473 | { | |
474 | register char *field_data; /* pointer to first byte */ | |
475 | /* of field data in tuple*/ | |
476 | register TUPLE_DESCRIPTOR tpd; /* pointer to descriptor */ | |
477 | register int i; /* counter of fields */ | |
478 | int num_fields; /* total number of fields */ | |
479 | ||
480 | /* | |
481 | * Return if no tuple at all supplied--perhaps this should be | |
482 | * an error. If supplied, make sure it looks like a tuple. | |
483 | */ | |
484 | ||
485 | if (t == NULL) | |
486 | return; | |
487 | ||
488 | GDB_CHECK_TUP(t, "initialize_tuple") | |
489 | ||
490 | /* | |
491 | * Set up to loop through fields: get tuple descriptor, field count | |
492 | * and pointer to first data byte in the tuple. | |
493 | */ | |
494 | ||
495 | tpd = t->desc; | |
496 | num_fields = tpd->field_count; | |
497 | field_data = t->data; /* address of first byte of */ | |
498 | /* user data */ | |
499 | ||
500 | ||
501 | /* | |
502 | * For each field in the tuple, loop calling its null value | |
503 | * initialization routine. | |
504 | */ | |
505 | ||
506 | for (i=0; i<num_fields; i++) { | |
507 | FCN_PROPERTY(tpd->var[i].type, NULL_PROPERTY) | |
508 | (field_data+tpd->var[i].offset); | |
509 | } | |
510 | } | |
511 | ||
512 | ||
513 | /*----------------------------------------------------------*/ | |
514 | /* | |
515 | /* null_tuple_strings (null_tuple_strings) | |
516 | /* | |
517 | /* Reclaim the space for all fields in the tuple | |
518 | /* whose type is 'string.' | |
519 | /* | |
520 | /*----------------------------------------------------------*/ | |
521 | ||
522 | int | |
523 | null_tuple_strings(t) | |
524 | TUPLE t; | |
525 | { | |
526 | register char *field_data; /* pointer to first byte of */ | |
527 | /* field data in tuple */ | |
528 | register TUPLE_DESCRIPTOR tpd; /* pointer to descriptor */ | |
529 | register int i; /* counter of fields */ | |
530 | int num_fields; /* total number of fields */ | |
531 | ||
532 | /* | |
533 | * Return if no tuple at all supplied--perhaps this should be | |
534 | * an error | |
535 | */ | |
536 | ||
537 | if (t == NULL) | |
538 | return; | |
539 | ||
540 | GDB_CHECK_TUP(t, "null_tuple_strings") | |
541 | ||
542 | /* | |
543 | * Set up to loop through fields: get tuple descriptor, field count | |
544 | * and pointer to first data byte in the tuple. | |
545 | */ | |
546 | ||
547 | tpd = t->desc; | |
548 | num_fields = tpd->field_count; | |
549 | field_data = t->data; /* address of first byte of */ | |
550 | /* user data */ | |
551 | ||
552 | /* | |
553 | * For each field in the tuple, loop calling its null value | |
554 | * initialization routine. | |
555 | */ | |
556 | ||
557 | for (i=0; i<num_fields; i++) { | |
558 | if(FIELD_TYPE_IN_TUPLE(tpd,i) == STRING_T && | |
559 | (*(char **)(field_data+FIELD_OFFSET_IN_TUPLE(tpd,i)))!=NULL) | |
560 | string_free((STRING *)(field_data+ | |
561 | FIELD_OFFSET_IN_TUPLE(tpd,i))); | |
562 | } | |
563 | } | |
564 | \f | |
565 | /************************************************************************/ | |
566 | /* | |
567 | /* RELATION MANAGEMENT | |
568 | /* | |
569 | /************************************************************************/ | |
570 | ||
571 | /*----------------------------------------------------------*/ | |
572 | /* | |
573 | /* create_relation (create_relation) | |
574 | /* | |
575 | /*----------------------------------------------------------*/ | |
576 | ||
577 | RELATION | |
578 | create_relation(desc) | |
579 | TUPLE_DESCRIPTOR desc; | |
580 | { | |
581 | register RELATION r; | |
582 | ||
583 | GDB_CHECK_TPD(desc, "create_relation") | |
584 | ||
585 | r = (RELATION)db_alloc(sizeof(struct rel_dat)); | |
586 | ||
587 | if (r == NULL) | |
588 | GDB_GIVEUP("create_relation (create_relation) could not get enough space.\n") | |
589 | ||
590 | /* | |
591 | * Fill in the empty relation. Create a null circular list | |
592 | * of tuples and also hang the description. | |
593 | */ | |
594 | ||
595 | r->id = GDB_REL_ID; | |
596 | ||
597 | r->first = (TUPLE)r; | |
598 | r->last = (TUPLE)r; | |
599 | r->desc = desc; | |
600 | REFERENCE_TUPLE_DESCRIPTOR(desc); /* bump the reference count */ | |
601 | ||
602 | return r; | |
603 | } | |
604 | ||
605 | /*----------------------------------------------------------*/ | |
606 | /* | |
607 | /* delete_relation | |
608 | /* | |
609 | /* Deletes the tuples which comprise a relation. | |
610 | /* For each tuple, it does a null-tuple-strings | |
611 | /* prior to deleting, but it does not yet handle | |
612 | /* any other non-contiguous data. | |
613 | /* | |
614 | /*----------------------------------------------------------*/ | |
615 | ||
616 | int | |
617 | delete_relation(rel) | |
618 | RELATION rel; | |
619 | { | |
620 | register TUPLE t, next; | |
621 | TUPLE_DESCRIPTOR desc; | |
622 | ||
623 | /* | |
624 | * Make sure a proper relation is supplied. | |
625 | */ | |
626 | ||
627 | if (rel == NULL) | |
628 | GDB_GIVEUP("delete_relation called with null relation") | |
629 | GDB_CHECK_REL(rel, "delete_relation") | |
630 | ||
631 | t = FIRST_TUPLE_IN_RELATION(rel); | |
632 | ||
633 | /*----------------------------------------------------------*/ | |
634 | /* | |
635 | /* Free all the tuples | |
636 | /* | |
637 | /*----------------------------------------------------------*/ | |
638 | ||
639 | while (t!=NULL) { | |
640 | null_tuple_strings(t); | |
641 | next = NEXT_TUPLE_IN_RELATION(rel, t); | |
642 | delete_tuple(t); | |
643 | t = next; | |
644 | } | |
645 | ||
646 | /*----------------------------------------------------------*/ | |
647 | /* | |
648 | /* Give back the memory for the relation | |
649 | /* | |
650 | /*----------------------------------------------------------*/ | |
651 | ||
652 | desc = DESCRIPTOR_FROM_RELATION(rel); | |
653 | db_free((char *)rel, sizeof(struct rel_dat)); | |
654 | delete_tuple_descriptor(desc); /* does a reference */ | |
655 | /* counted delete */ | |
656 | } | |
657 | ||
658 | /*----------------------------------------------------------*/ | |
659 | /* | |
660 | /* tuples_in_relation | |
661 | /* | |
662 | /* Returns number of tuples in a relation. | |
663 | /* | |
664 | /* | |
665 | /*----------------------------------------------------------*/ | |
666 | ||
667 | int | |
668 | tuples_in_relation(rel) | |
669 | RELATION rel; | |
670 | { | |
671 | register int count; | |
672 | register RELATION r=rel; | |
673 | register TUPLE t; | |
674 | ||
675 | count = 0; | |
676 | ||
677 | for (t=FIRST_TUPLE_IN_RELATION(r); | |
678 | t != NULL; | |
679 | t=NEXT_TUPLE_IN_RELATION(r,t)) { | |
680 | count++; | |
681 | } | |
682 | return count; | |
683 | } | |
684 | ||
685 | ||
686 |