Scalable Distributed Data Structure Extension To Monet
0.4
Jonas S Karlsson
1997-07-18
Table Of Contents
1 SDDS implications of Monet
This module implements basic functionality concerning Scalable
Distributed Data Structures (SDDSs), LH*(LH) in particular. It
provides:
- The Client Data for managing the image of the client.
- The Client Algorithms used for calculating the most
likely server node that stores one piece of data.
- The Server List that is the mapping from logical server
number to a physical machine identity.
1.1 Todo:
- An implementation of the client data functions.
- The client algorithms for address calculations.
- The server algorithms for address calculations.
- How to handle local server data. (general interface). What types?
- A simulated SDDS with client/server on the same node.
1.2 Discussion about ATOMic values in MONET
Interesting enough is that during MIL execution the atomic values
are (often) copied. It is undefined from the semantics when values
are copied or not. This means that an atom should not be modified
in place since there is no guarantees that the /actual/ instance
of ATOM is the one you have a reference to.
This gives a few possibilities for implementation of a "record"
of information:
- Decluster the attributes of the object/record (typical monet style).
This approach is tedious since there will be many tables -- one
for each attribute. This is good if one wants to make queries
over the SDDS meta-information. I cannot really forsee that.
- Store the "state" as an ATOM. Replace atom then there is a new
state. Updates will be faster (better concurrency) than in the
decluster approach. A potential problem is that since the
state is copied, updates might take longer to propagate.
(since copies are passed around during processing)
I've decied to go for the second approach. It might not be
the best. But it is not used in too much in MONET. So
why not... '
Locking should be used when looking up the ATOM from
the BAT and also when updating it. But that is an
"application problem" (according to MK). Maybe
it means that MIL code (advanced operations, should
do this themselves). Since the actual update or
atom lookup can be implemented by two interface functions
I (jonas) think they should do the locking. Locks for
this data is expected to be cheap, especially since this
gives (eventually, maybe it should be required?) a copy
of the ATOM.
The other (more MONETY) alternative is to create
atoms, and not update them. The update returns
a new up-to-date atom that the client inserts.
1.3 Structure
There are two new MONET ATOMic values; LHSLH is the
client information; LHSLH_SERVER is the server side
information. The latter "points"(should lookup from table)
to a LHSLH_CLIENT.
At each site (both client and servers) there is some
global tables -- sdds_clients that stores client atoms,
and sdds_servers that stores server atoms.
And there is a mapping from global logical machine number
(sdds_machines) to a physcial instance (at the moment
"tcpipname:port").
92
93 MODULE sdds;
94 USE tcpip;
1.4 Atom Declarations
1.4.1 LH*lh Client Image
The client images data, such as $i$ and $p$, which hashfunction
and what Server List (mapping) that is used. For each file
there is one (at least) client image at the server site.
2 The SDDS FILE class
104
105 #ifndef __LHSLH_H__
106 #define __LHSLH_H__
107
108 #define _DEBUG_
109
110 typedef struct lhslh {
111 unsigned long id;
112 unsigned int level;
113 unsigned int split;
114 bat mappingid;
115 } lhslh;
116
117 typedef struct lhslh_server {
118 lhslh* client; /* BAD! */
119 bat batid;
120 int my_number;
121 } lhslh_server;
122
123
2.1 Handy macros
This is some handy macros for structure elements access
129
130 #define GETID(lhslh) ((lhslh)->id)
131 #define GETLEVEL(lhslh) ((lhslh)->level)
132 #define GETNEXT_TO_SPLIT(lhslh) ((lhslh)->split)
133 #define GETmapping(lhslh) ((lhslh)->mappingid)
134 #define GETMAPPING(lhslh) (BATdescriptor(GETmapping(lhslh)))
135
136
137 #define GETbat(lhslh_s) ((lhslh_s)->batid)
138 #define GETBAT(lhslh_s) (BATdescriptor(GETbat(lhslh_s)))
139 #define GETCLIENT(lhslh_s) ((lhslh_s)->client)
140 #define GETMY_NUMBER(lhslh_s) ((lhslh_s)->my_number)
141
142 #endif /* __LHSLH_H__ */
The atom slot-in functions.
146
147 ATOM lhslh[16, 0];
148 TOSTR = lhslh_tostr;
149 FROMSTR = lhslh_fromstr;
150 COMP = lhslh_compare;
151 HASH = lhslh_hash;
152 NULL = lhslh_null;
153 END;
2.1.1 LH*lh Server Data
The server specific data is stored in the lhslh_server atom.
158
159 ATOM lhslh_server[12, 0];
160 TOSTR = lhslh_server_tostr;
161 FROMSTR = lhslh_server_fromstr;
162 COMP = lhslh_server_compare;
163 HASH = lhslh_server_hash;
164 NULL = lhslh_server_null;
165 END;
166
167
2.2 Command Declarations
It is assumed that the SDDS-file already exists (at some server). The
handle, a means of access to the data in the file, is given by calling
the function LHSLH(ID, NAME)
The file must currently be created at a server node with
LHSLH_NEW(HEADTYPE, TAILTYPE), the structure is returned. File
parameters are set using SPLIT_AT(LHSLH, COUNT). COUNT determines at
what point the file should be split. Another measure is to set the
maximum memory usage using SPLIT_SIZE(LHSLH, BYTES).
2.2.1 Admin
Client side functions
182
183 COMMAND lhslh_open(int id, any home): lhslh = lhslh_open;
184 "open a handle to a SDDS (LH*) named ID/int at SERVER/int"
185
186 COMMAND close(lhslh) = lhslh_close;
187 "close the HANDLE/lhslh"
188
189 COMMAND "destroy"(lhslh f) = lhslh_destroy;
190 "kills the whole file, the question is if the client should
191 be able to do this?"
192
Server side functions.
196
197 COMMAND lhslh_server_new(int h, int t, int id, any home, int my_number): lhslh_server = lhslh_server_new;
198 "creates an lhslh table at this node."
199
2.2.2 Info
202
203 COMMAND level(lhslh): int = lhslh_level;
204 "the client image of the level of the SDDS"
205
206 COMMAND next_to_split(lhslh): int = lhslh_next_to_split;
207 "the client image of the next server to split of the named SDDS"
208
209 COMMAND mapping_ref(lhslh): bat[int, int] = lhslh_mapping_ref;
210 "returns a (reference to a) BAT containing the mapping from logical
211 local number (to the SDDS) to a global number (identity)."
212
213 COMMAND address(lhslh, any): int = lhslh_address;
214 "calculates the address for data of KEY, for a client"
215
216 COMMAND address(lhslh_server, any): int = lhslh_server_address;
217 "calculates the address for data of KEY, at a server"
218
219 COMMAND id(lhslh): int = lhslh_id;
220 "returns the id of the file"
221
222 COMMAND my_number(lhslh_server s): int = lhslh_server_my_number;
223 "the logical number of the server s for the LH*LH-file"
224 COMMAND my_number(lhslh_server s, int) = lhslh_server_my_number_set;
225 "set it"
226
227 COMMAND split_at(lhslh_server s): int = lhslh_split_at;
228 "get the number of items that the server should split at"
229 COMMAND split_at(lhslh_server s, int at) = lhslh_split_at_set;
230 "set the number of items that the server should split at"
231
232 COMMAND client(lhslh_server s): lhslh = lhslh_server_client;
233 "get the handle to the client structure from a server."
234
235 COMMAND bat_ref(lhslh_server s): bat[any, any] = lhslh_server_bat_ref;
236 "get the handle to the bat for the server."
237
2.2.3 Modifiers
Client side
241
242 COMMAND update_image(lhslh, int level, int next) = lhslh_update_image;
243 "returns an up-to-date atom for the client image of FILE with LEVEL and SPLITP. \\
244 NOTE: The returned/modified atom should be inserted into a table otherwise it
245 will be forgotten."
246
247 COMMAND outdated_image_Q(lhslh, int level, int next) = lhslh_outdated_image;
248 "returns true if the first image is considered to be outdated
249 compared to the the second given image. The given image is assumed
250 to be the 'official' of the data structure. In the case of LH*LH only
251 the level is transferred from the second image. The client is
252 not allowed to use what server the response the answer came from
253 (see Jonas S Karlsson's Licentiate Eng. Theses)."
254
2.2.4 BAT style interface
Client side
258 COMMAND "insert"(lhslh f, any h, any t) : bit = lhslh_insert_h_t;
259 "client insert in the distributed bat LH*LH-file f an BUN with head h and tail t"
260
261 COMMAND "insert"(lhslh f, bat[any, any]) = lhslh_insert_bat;
262 "client insert in the LH*LH-file f the items in the bat."
263
264 COMMAND "delete"(lhslh f, any::1 h): bat[any::1, any] = lhslh_delete_h;
265 "client delete BUNs stored in LH*LH-file f, with head h."
266
Server side
2.2.5 Selectors/Operators
2.2.6 BAT style interface
Server side
277 COMMAND select(lhslh f, any::1 low, any::1 high): bat[any, any::1] = lhslh_select_l_h;
278 "a text"
279 COMMAND select(lhslh f, any::1): bat[any, any::1] = lhslh_select_eq;
280 "they only select on tails???"
281 COMMAND sample(lhslh f, int num): bat[any::1,any::2] = lhslh_sample;
282 "Produce a random selection of size 'num' from the input BAT."
283
2.2.7 Joins
2.2.8 Aggregates
# this bat keeps track of all the clients interface atoms that
# this Monet instance knows about, they are shared both for
# SDDS server/clients.
# this bat keeps track of the different servers/buckets stored at
# this Monet instance (server).
source(\"/ufs/jonas/Src/Module/lhs/machines.mil\");
# if (client.need_updating_Q(server.client)) {
# sender.rpc(\"update_image\", client, server.official_image);
# }
302
303 LOAD = "
304 ";
305
2.3 Server side functions
These are server side functions, to be called remotely
common arguments:
SENDER : Includes id of sender/reqid can be used to reply
CLIENT : The client atom lhslh/rpsrp or whatever.
It contains at least all information
we need from the client side for IAMs.
...parameters
These functions assume that the servers data is stored
in something that behaves like a bat. That is operations
like .insert a.s.o. are defined upon the /ref_bat/s result.
if an RPC funtion returns it first argument, then
this should be interpreted as /no result/ - Send no
result back. The result from the query does then
come from another sender than the one who orignally
got the query.
How should a program that want to do queries
look like? Explicit code sent out? I still
think query execution is a dataflow issue.
the data comes from somewhere and should end up
somewhere, possible not the same. This more or
less means that /the data should know where to
go/.
this means that a /query plan/ with input & output pipes
should be sent. Actually into a pipe should go first
indata, /program/, outputing statements that distribute
the data to where it is needed together maybe with a /program/.
Sort of continuation based...
341
342 END sdds;
343
344
3 Methods for lhslh
3.1 General Includes
C
353 #include <stdlib.h>
354 #include <math.h>
355 #include <limits.h>
Monet
359 #include <gdk.h>
360 #include <monet.h>
SDDS
364 #include "sdds.h"
365 #include "sdds.proto.h"
LH*LH
369 #include "LHS/hash.h"
370
More handy things, like arbitrary constants...
374
375 #define LHSLH_INITIAL_BAT_SIZE 16
376 #define LHSLH_INITIAL_MAPPING_SIZE 16
377 #define TOSTRING_SIZE 64 /* I don't like this, unsafe (sprintf) */
378
3.1.1 General need
GDKmemsure ensures that (void*) MEMORY, (int*) LENGTH bytes, can contain a structure of
at least NEED bytes. If to small/NULL/unspecified, the memory is GDKfreed, and the reallocated
with GDKmalloc according to the specified needs. If non-NULL length, then it is set
to the actual length.
387 void *GDKmemsure(void *mem, int *len, int need)
388 {
389 if (mem && (!len || (*len < need))) { /* To small or no lenght spec. */
390 GDKfree(mem); mem = NULL;
391 }
392 if (!mem) { /* no mem (maby it was to small) */
393 mem = GDKmalloc(need);
394 if (len) *len = need;
395 }
396 return mem;
397 }
398
399
3.1.2 Monet ATOMS required operators
3.1.3 LH*lh Client Image
404
405 static lhslh lhslh_nil = { 0 };
406
407 int lhslh_tostr(str *s, int *l, lhslh *h)
408 {
409 char *buf;
410 *s = (str) GDKmemsure(*s, l, TOSTRING_SIZE);
411 buf = *s;
412 sprintf(buf, "<lhslh %d: L=%d, P=%d>",
413 GETID(h), GETLEVEL(h), GETNEXT_TO_SPLIT(h));
414 return strlen(buf);
415 }
416
417 int lhslh_fromstr(str s, int *l, lhslh **h)
418 {
419 int id, level, p, len;
420
421 if (sscanf(s, "<lhslh %d: L=%d, P=%d>%n", &id, &level, &p, &len) == 3) {
422 if( *h == NULL || (*l) < (int)sizeof(lhslh)) {
423 /* Should one not do a GDKfree here if *h is no null? */
424 *h = (lhslh *)GDKmalloc(sizeof(lhslh));
425 *l = sizeof(lhslh);
426 GETID(*h)=id;
427 GETLEVEL(*h)=level;
428 GETNEXT_TO_SPLIT(*h)=p;
429 }
430 return len; /* Characters consumed */
431 }
432 return 0; /* failed */
433 }
434
435 int lhslh_hash( lhslh *h )
436 {
437 return GETID(h); /* Not the best but... */
438 }
439
440 lhslh* lhslh_null( void )
441 {
442 return &lhslh_nil;
443 }
444
445 int lhslh_compare ( lhslh *h1, lhslh *h2 )
446 {
447 return (GETID(h1)!=GETID(h2));
448 }
449
3.1.4 LH*lh Server Data
[ To be filled in ]
lhslh_server_tostring & lhslh_server_fromstr are not "good".
455
456 static lhslh_server lhslh_server_nil = { 0 };
457
458 int lhslh_server_tostr(str *s, int *l, lhslh_server *h)
459 {
460 char *buf;
461
462 *s = (str) GDKmemsure(*s, l, TOSTRING_SIZE);
463 buf = *s;
464 sprintf(buf, "<lhslh_server %d.%d: L=%d, P=%d>",
465 GETID(GETCLIENT(h)), GETMY_NUMBER(h), GETLEVEL(GETCLIENT(h)), GETNEXT_TO_SPLIT(GETCLIENT(h)));
466 return strlen(buf);
467 }
468
469 int lhslh_server_fromstr(str s, int *l, lhslh_server **h)
470 {
471 int id, level, p, len;
472
473 if (sscanf(s, "<lhslh_server %d: L=%d, P=%d>%n", &id, &level, &p, &len) == 3) {
474 if( *h == NULL || (*l) < (int)sizeof(lhslh)) {
475 /* Should one not do a GDKfree here if *h is no null? */
476 *h = (lhslh_server *)GDKmalloc(sizeof(lhslh_server));
477 *l = sizeof(lhslh_server);
478 GETID(GETCLIENT(*h)) = id;
479 GETLEVEL(GETCLIENT(*h)) = level;
480 GETNEXT_TO_SPLIT(GETCLIENT(*h)) = p;
481 }
482 return len; /* Characters consumed */
483 }
484 return 0; /* failed */
485 }
486
487 int lhslh_server_hash( lhslh_server *h )
488 {
489 return GETID(GETCLIENT(h));
490 }
491
492 lhslh_server* lhslh_server_null( void )
493 {
494 return &lhslh_server_nil;
495 }
496
Differens between comp/compare?
500
501 int lhslh_server_compare ( lhslh_server *h1, lhslh_server *h2 )
502 {
503 return (GETID(GETCLIENT(h1))!=GETID(GETCLIENT(h2)));
504 }
505
3.2 Actual Command Implementation
3.2.1 Admin
Client side functions
511
512 int lhslh_open(lhslh *res, int *id, ptr home, int home_tp)
513 {
514 printf("sizeof lhslh = %d\n", sizeof(lhslh));
515 printf("sizeof lhslh_server = %d\n", sizeof(lhslh_server));
516
517 GETID(res) = *id;
518 GETLEVEL(res) = 0;
519 GETNEXT_TO_SPLIT(res) = 0;
520 {
521 BAT* b = BATnew(TYPE_int, TYPE_int, LHSLH_INITIAL_MAPPING_SIZE);
522 GETmapping(res) = BBPcacheid(b);
523 BBPfix(GETmapping(res));
524 }
525 /* serverhome not used, should be stored in a bat
526 and we keep a ref to the bat from here! */
527 return GDK_SUCCEED;
528 }
529
530 int lhslh_close(lhslh *h)
531 {
532 return GDK_SUCCEED;
533 }
534
535 int lhslh_destroy(lhslh *h)
536 {
537 return GDK_SUCCEED;
538 }
539
Server side functions.
543
544 int
545 lhslh_server_new(
546 lhslh_server* retval, /* put return atom here. */
547 int* h, /* pointer to integer.*/
548 int* t, /* pointer to integer.*/
549 int* id,
550 ptr home,
551 int home_tp,
552 int* my_number
553 )
554 {
555 BAT* b = BATnew(*h, *t, LHSLH_INITIAL_BAT_SIZE);
556 b = BATmode(b, PERSISTENT); /* OR PERMANENT??? */
557 GETbat(retval) = BBPcacheid(b);
558 BBPfix(GETbat(retval));
559 {
560 char name[1024];
561
562 sprintf(name, "sdds_%d_%d", *id, *my_number);
563 BATrename(b, name);
564 }
This is funny by the way, since we might have several "servers" for the same SDDS-file
on the same Mserver. Should we still store CLIENT-information per "server" or per
Mserver (database)? How would a better influence the SDDS: LH*(lh)? Guesses:
For servers operations not at all. But the updating schema could have to be
different, but all clients would benefit from a better image. (The problem might
lie in a splitting server).
573 GETCLIENT(retval) = GDKmalloc(sizeof(lhslh)); /* should be stored in a bat? It may*/
574 GETMY_NUMBER(retval) = *my_number;
575 return lhslh_open(GETCLIENT(retval), id, home, home_tp);
576 }
577
3.2.2 Info
580
581 int lhslh_level(int *res, lhslh *h)
582 {
583 *res = GETLEVEL(h);
584 return GDK_SUCCEED;
585 }
586
587 int lhslh_next_to_split(int *res, lhslh *h)
588 {
589 *res = GETNEXT_TO_SPLIT(h);
590 return GDK_SUCCEED;
591 }
592
593 int lhslh_mapping_ref(BAT **bat, lhslh *h)
594 {
595 *bat = GETMAPPING(h);
596 return GDK_SUCCEED;
597 }
598
599 /* #define ATOMhash_correct(typ, key, max) ((unsigned)BATatoms[typ].atomHash(key, max)) */
600
601 int lhslh_address(int *res, lhslh *h, void *key, int typ)
602 {
603 /* *res = 4711+typ; stupid variant to test */
604 if (!(BATatoms[typ].atomHash)) {
605 printf("lhslh_address: There is no hash-function for GDK type = %d\n", typ);
606 *res = -1;
607
608 return GDK_FAIL;
609 }
610 *res = AddressOfKey(ATOMhash(typ, key), GETLEVEL(h), GETNEXT_TO_SPLIT(h));
611 printf("lhslh_address finished *res = %d\n", *res);
612 return GDK_SUCCEED;
613 }
614
615 int lhslh_server_address(int *res, lhslh_server *h, void *key, int typ)
616 {
617 *res = ServerAddrCalc(GETMY_NUMBER(h), ATOMhash(typ, key),
618 GETLEVEL(GETCLIENT(h)));
619 return GDK_SUCCEED;
620 }
621
622
623 int lhslh_server_client(lhslh *client, lhslh_server *h)
624 {
625 *client = *GETCLIENT(h);
626 return GDK_SUCCEED;
627 }
628
629 int lhslh_server_bat_ref(BAT **bat, lhslh_server *h)
630 {
631 *bat = GETBAT(h);
632 return GDK_SUCCEED;
633 }
634
3.2.3 Modifiers
637
638 /* #include "../tcpip/tcpip.h" */
639
640 #include "../tcpip/tcpip.h" /* Hmm, copied file... (BAD) */
641
Updates should also require that the sdds_servers should be
updated if needed. That is more mappings from logical number to
names of physical machines should be added.
649 int lhslh_update_image(lhslh* h, int* level, int* next)
650 {
651 GETLEVEL(h) = *level;
652 GETNEXT_TO_SPLIT(h) = *next;
653 return GDK_SUCCEED;
654 }
655
656 int lhslh_outdated_image(lhslh* h, int* level, int* next)
657 {
658 GETLEVEL(h) = *level;
659 GETNEXT_TO_SPLIT(h) = *next;
660 return GDK_SUCCEED;
661 }
662
Addressing nodes is a difficult issue. There are three differnt
types of addresses:
- Logical (LH*)
- Virtual (logical machine)
- Physical (machine IP-address)
The logical addresses are the ones calculated by an SDDS they
span [0..n-1] wheren n is the number of servers employed. This
under the assumption that there is a linear growth (or a mapping
to a linear scale) for the used SDDS algorithm.
The function mapping_ref(lhslh) returns the virtual address for
an logical address per LH* structure. This mapping might vary
between different invocations of the database system.
The virtual addresses (machine OIDs) are decided at bucket allocation
time. A server of an SDDS can be a server for many buckets from the
same or other SDDS relations (files). For SDDSs using the same key for
distribution they might share the same/similar mapping_ref.
A global BAT contains the mapping from a virtual address (OID) to
a physical machine by means of an "internet-address".
One of the reasons for using virtual addresses is that of recoverbility;
If a server for some reason gets disconnected from the others,
a decision can be made to let another node take over the load
or use a spare node; Another reason is that at one run, a certain
number of workstation might be availiable and at another run
a different number. Then this mapping could serve as the mean
of not having to redistribute the data because of different environment,
it should work anyway. It also allows dynamic redistribution. Just change
and distribute the mapping.
An example:
a.SDDS-log : 0, 1, 2, 3, 4, 5, 6, 7
a.mach-virt: 1, 2, 3, 1, 2, 3, 1, 2
b.SDDS-log : 0, 1, 2, 3, 4, 5, 6, 7
b.mach-virt: 1, 2, 3, 1, 2, 3, 1, 2
c.SDDS-log : 0, 1, 2, 3, 4, 5, 6, 7
c.mach-virt: 4, 5, 4, 5, 4, 5, 4, 5
mach-phys : 1 -- skiff, 2 -- pinas, 3 -- stomer, 4,5 -- skiff:4711, 4712
a) and b) use the same distribution because they are indexed on
the same key, this clusters "objects".
c), however, uses a different distribution, at the moment giving
allocated to two virtual nodes (4, 5) that both happends to be
the same physcical nod (skiff)! But they use two different ports,
thus two threads, and enables two requests to be handled at the same time!
The two threads are guaranteed to use different sets of data.
Question: How much overhead does it impose to let a server handle
several "independent" buckets compared to the exact match of number
of buckets? How does it affect "load" (as amount of used storage,
execution throughput).
727
728 int lhslh_log2virt(int* log, lhslh *h, int *virt)
729 {
730 *log = 0; /* Fallback case! should always work! */
731
732 *log = *virt; /* for testing */
733 return GDK_SUCCEED;
734 }
735
736 int lhslh_virt2phys(int* phys, int *virt)
737 {
738 *phys = -1;
739 return GDK_SUCCEED;
740 }
741
743 int
744 lhslh_insert_bat(
745 lhslh* f, /* pointer to user-defined atom.*/
746 BAT* bat /* pointer to BAT[any,any] record.*/
747 )
748 {
749 return GDK_FAIL;
750 }
751
752 int fix_address(lhslh *h, int logical) { /* log -> virt -> phys -> connhandle */
753 int virt, phys;
754 int zelf = 0;
755 ptr *res;
756 int *res_tp;
757
758 RFC(res, res_tp, zelf, "log2comm",
759 TPE(h, lhslh); TPE(&logical, int););
760 return *((int*) res);
761 }
762
763
764 int
765 lhslh_insert_h_t(
766 bit* retval, /* put return atom here. */
767 lhslh* f, /* pointer to user-defined atom.*/
768 ptr h, /* ANY parameter: pointer to some atom.*/
769 int h_tp, /* type number of previous parameter.*/
770 ptr t, /* ANY parameter: pointer to some atom.*/
771 int t_tp /* type number of previous parameter.*/
772 )
773 {
774 ptr *res;
775 int *res_tp;
776 char buf[256];
777 int zero = 0;
778
779 RFC(res, res_tp, 0 /* fix */, "sdds_insert",
780 tINT(&zero);
781 tVAL(f);
782 tARG(h, h_tp);
783 tARG(t, t_tp));
784
785 /*
786 sprintf(name, "sdds.find(%i).bat_ref.insert", GETID(f));
787 RFC(res, res_tp, name, ARG(h, h_tp); ARG(t, t_tp));
788 */
789 return GDK_SUCCEED;
790 }
791
792 int
793 lhslh_delete_h(
794 BAT** retval, /* put pointer to BAT[any,any] record here. */
795 lhslh* f, /* pointer to user-defined atom.*/
796 ptr h, /* ANY parameter: pointer to some atom.*/
797 int h_tp /* type number of previous parameter.*/
798 )
799 {
800 return GDK_SUCCEED;
801 }
802
3.2.4 Selectors/Operators
805
806 int
807 lhslh_select_eq(
808 BAT** retval, /* put pointer to BAT[any,any] record here. */
809 lhslh* f, /* pointer to user-defined atom.*/
810 ptr Void1, /* ANY parameter: pointer to some atom.*/
811 int Tpe1 /* type number of previous parameter.*/
812 )
813 {
814 return GDK_SUCCEED;
815 }
816
817 int
818 lhslh_select_l_h(
819 BAT** retval, /* put pointer to BAT[any,any] record here. */
820 lhslh* f, /* pointer to user-defined atom.*/
821 ptr low, /* ANY parameter: pointer to some atom.*/
822 int low_tp, /* type number of previous parameter.*/
823 ptr high, /* ANY parameter: pointer to some atom.*/
824 int high_tp /* type number of previous parameter.*/
825 )
826 {
827 return GDK_SUCCEED;
828 }
829
830 int
831 lhslh_sample(
832 BAT** retval, /* put pointer to BAT[any,any] record here. */
833 lhslh* f, /* pointer to user-defined atom.*/
834 int* num /* pointer to integer.*/
835 )
836 {
837 return GDK_SUCCEED;
838 }
839
840
3.2.5 Joins
3.2.6 Aggregates