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