OSDN Git Service

fd49fbe5af89f09fe71b7e83171bd1e1cb2c5781
[pg-rex/syncrep.git] / src / backend / utils / fmgr / dfmgr.c
1 /*-------------------------------------------------------------------------
2  *
3  * dfmgr.c
4  *        Dynamic function manager code.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.23 1999/02/13 23:19:51 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #include "postgres.h"
20
21 #include "config.h"                             /* for MAXPATHLEN */
22 #include "fmgr.h"                               /* generated by Gen_fmgrtab.sh */
23 #include "utils/dynamic_loader.h"
24 #include "utils/elog.h"
25 #include "utils/builtins.h"
26 #include "access/heapam.h"
27 #include "nodes/pg_list.h"
28
29 #include "dynloader.h"
30
31 #ifdef __ultrix
32 #include <dl.h>
33 #endif
34
35 #include "catalog/catname.h"
36 #include "utils/syscache.h"
37 #include "catalog/pg_proc.h"
38
39 static DynamicFileList *file_list = (DynamicFileList *) NULL;
40 static DynamicFileList *file_tail = (DynamicFileList *) NULL;
41
42 #define NOT_EQUAL(A, B) (((A).st_ino != (B).inode) \
43                                           || ((A).st_dev != (B).device))
44
45 static Oid      procedureId_save = -1;
46 static int      pronargs_save;
47 static func_ptr user_fn_save = (func_ptr) NULL;
48 static func_ptr handle_load(char *filename, char *funcname);
49
50 func_ptr
51 fmgr_dynamic(Oid procedureId, int *pronargs)
52 {
53         HeapTuple       procedureTuple;
54         Form_pg_proc procedureStruct;
55         char       *proname,
56                            *probinstring;
57         Datum           probinattr;
58         func_ptr        user_fn;
59         Relation        rel;
60         bool            isnull;
61
62         if (procedureId == procedureId_save)
63         {
64                 *pronargs = pronargs_save;
65                 return user_fn_save;
66         }
67
68         /*
69          * The procedure isn't a builtin, so we'll have to do a catalog lookup
70          * to find its pg_proc entry.
71          */
72         procedureTuple = SearchSysCacheTuple(PROOID,
73                                                                                  ObjectIdGetDatum(procedureId),
74                                                                                  0, 0, 0);
75         if (!HeapTupleIsValid(procedureTuple))
76         {
77                 elog(ERROR, "fmgr: Cache lookup failed for procedure %d\n",
78                          procedureId);
79                 return (func_ptr) NULL;
80         }
81
82         procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
83         proname = procedureStruct->proname.data;
84         pronargs_save = *pronargs = procedureStruct->pronargs;
85
86         /*
87          * Extract the procedure info from the pg_proc tuple. Since probin is
88          * varlena, do a amgetattr() on the procedure tuple.  To do that, we
89          * need the rel for the procedure relation, so...
90          */
91
92         /* open pg_procedure */
93
94         rel = heap_openr(ProcedureRelationName);
95         if (!RelationIsValid(rel))
96         {
97                 elog(ERROR, "fmgr: Could not open relation %s",
98                          ProcedureRelationName);
99                 return (func_ptr) NULL;
100         }
101         probinattr = heap_getattr(procedureTuple,
102                                                           Anum_pg_proc_probin,
103                                                           RelationGetDescr(rel), &isnull);
104         if (!PointerIsValid(probinattr) /* || isnull */ )
105         {
106                 heap_close(rel);
107                 elog(ERROR, "fmgr: Could not extract probin for %d from %s",
108                          procedureId, ProcedureRelationName);
109                 return (func_ptr) NULL;
110         }
111         probinstring = textout((struct varlena *) probinattr);
112
113         user_fn = handle_load(probinstring, proname);
114
115         procedureId_save = procedureId;
116         user_fn_save = user_fn;
117
118         return user_fn;
119 }
120
121 static func_ptr
122 handle_load(char *filename, char *funcname)
123 {
124         DynamicFileList *file_scanner = (DynamicFileList *) NULL;
125         func_ptr        retval = (func_ptr) NULL;
126         char       *load_error;
127         struct stat stat_buf;
128
129         /*
130          * Do this because loading files may screw up the dynamic function
131          * manager otherwise.
132          */
133         procedureId_save = -1;
134
135         /*
136          * Scan the list of loaded FILES to see if the function has been
137          * loaded.
138          */
139
140         if (filename != (char *) NULL)
141         {
142                 for (file_scanner = file_list;
143                          file_scanner != (DynamicFileList *) NULL
144                          && file_scanner->filename != (char *) NULL
145                          && strcmp(filename, file_scanner->filename) != 0;
146                          file_scanner = file_scanner->next)
147                         ;
148                 if (file_scanner == (DynamicFileList *) NULL)
149                 {
150                         if (stat(filename, &stat_buf) == -1)
151                                 elog(ERROR, "stat failed on file %s", filename);
152
153                         for (file_scanner = file_list;
154                                  file_scanner != (DynamicFileList *) NULL
155                                  && (NOT_EQUAL(stat_buf, *file_scanner));
156                                  file_scanner = file_scanner->next)
157                                 ;
158
159                         /*
160                          * Same files - different paths (ie, symlink or link)
161                          */
162                         if (file_scanner != (DynamicFileList *) NULL)
163                                 strcpy(file_scanner->filename, filename);
164
165                 }
166         }
167         else
168                 file_scanner = (DynamicFileList *) NULL;
169
170         /*
171          * File not loaded yet.
172          */
173
174         if (file_scanner == (DynamicFileList *) NULL)
175         {
176                 if (file_list == (DynamicFileList *) NULL)
177                 {
178                         file_list = (DynamicFileList *)
179                                 malloc(sizeof(DynamicFileList));
180                         file_scanner = file_list;
181                 }
182                 else
183                 {
184                         file_tail->next = (DynamicFileList *)
185                                 malloc(sizeof(DynamicFileList));
186                         file_scanner = file_tail->next;
187                 }
188                 MemSet((char *) file_scanner, 0, sizeof(DynamicFileList));
189
190                 strcpy(file_scanner->filename, filename);
191                 file_scanner->device = stat_buf.st_dev;
192                 file_scanner->inode = stat_buf.st_ino;
193                 file_scanner->next = (DynamicFileList *) NULL;
194
195                 file_scanner->handle = pg_dlopen(filename);
196                 if (file_scanner->handle == (void *) NULL)
197                 {
198                         load_error = (char *) pg_dlerror();
199                         if (file_scanner == file_list)
200                                 file_list = (DynamicFileList *) NULL;
201                         else
202                                 file_tail->next = (DynamicFileList *) NULL;
203
204                         free((char *) file_scanner);
205                         elog(ERROR, "Load of file %s failed: %s", filename, load_error);
206                 }
207
208                 /*
209                  * Just load the file - we are done with that so return.
210                  */
211                 file_tail = file_scanner;
212
213                 if (funcname == (char *) NULL)
214                         return (func_ptr) NULL;
215         }
216
217         retval = (func_ptr) pg_dlsym(file_scanner->handle, funcname);
218
219         if (retval == (func_ptr) NULL)
220                 elog(ERROR, "Can't find function %s in file %s", funcname, filename);
221
222         return retval;
223 }
224
225 /*
226  * This function loads files by the following:
227  *
228  * If the file is already loaded:
229  * o  Zero out that file's loaded space (so it doesn't screw up linking)
230  * o  Free all space associated with that file
231  * o  Free that file's descriptor.
232  *
233  * Now load the file by calling handle_load with a NULL argument as the
234  * function.
235  */
236 void
237 load_file(char *filename)
238 {
239         DynamicFileList *file_scanner,
240                            *p;
241         struct stat stat_buf;
242
243         int                     done = 0;
244
245         if (stat(filename, &stat_buf) == -1)
246                 elog(ERROR, "stat failed on file %s", filename);
247
248         if (file_list != (DynamicFileList *) NULL
249                 && !NOT_EQUAL(stat_buf, *file_list))
250         {
251                 file_scanner = file_list;
252                 file_list = file_list->next;
253                 pg_dlclose(file_scanner->handle);
254                 free((char *) file_scanner);
255         }
256         else if (file_list != (DynamicFileList *) NULL)
257         {
258                 file_scanner = file_list;
259                 while (!done)
260                 {
261                         if (file_scanner->next == (DynamicFileList *) NULL)
262                                 done = 1;
263                         else if (!NOT_EQUAL(stat_buf, *(file_scanner->next)))
264                                 done = 1;
265                         else
266                                 file_scanner = file_scanner->next;
267                 }
268
269                 if (file_scanner->next != (DynamicFileList *) NULL)
270                 {
271                         p = file_scanner->next;
272                         file_scanner->next = file_scanner->next->next;
273                         pg_dlclose(file_scanner->handle);
274                         free((char *) p);
275                 }
276         }
277         handle_load(filename, (char *) NULL);
278 }
279
280 /* Is this used? bjm 1998/10/08   No. tgl 1999/02/07 */
281 #ifdef NOT_USED
282 func_ptr
283 trigger_dynamic(char *filename, char *funcname)
284 {
285         func_ptr        trigger_fn;
286
287         trigger_fn = handle_load(filename, funcname);
288
289         return trigger_fn;
290 }
291 #endif