dfmgr.c 5.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * dfmgr.c
4
 *	  Dynamic function manager code.
5
 *
B
Add:  
Bruce Momjian 已提交
6 7
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.45 2000/11/16 22:30:34 tgl Exp $
12 13 14 15 16 17 18 19 20
 *
 *-------------------------------------------------------------------------
 */
#include <sys/types.h>
#include <sys/stat.h>

#include "postgres.h"

#include "catalog/pg_proc.h"
B
Bruce Momjian 已提交
21
#include "dynloader.h"
22
#include "utils/dynamic_loader.h"
B
Bruce Momjian 已提交
23
#include "utils/builtins.h"
B
Bruce Momjian 已提交
24
#include "utils/syscache.h"
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

/*
 * List of dynamically loaded files.
 */

typedef struct df_files
{
	struct df_files *next;		/* List link */
	dev_t		device;			/* Device file is on */
	ino_t		inode;			/* Inode number of file */
	void	   *handle;			/* a handle for pg_dl* functions */
	char		filename[1];	/* Full pathname of file */
	/* we allocate the block big enough for actual length of pathname.
	 * filename[] must be last item in struct!
	 */
} DynamicFileList;

43 44 45
static DynamicFileList *file_list = (DynamicFileList *) NULL;
static DynamicFileList *file_tail = (DynamicFileList *) NULL;

46
#define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
47 48


49 50
PGFunction
fmgr_dynamic(Oid functionId)
51
{
52 53
	HeapTuple	procedureTuple;
	Form_pg_proc procedureStruct;
54
	char	   *proname,
55
			   *prosrcstring,
56
			   *probinstring;
57 58 59
	Datum		prosrcattr,
				probinattr;
	PGFunction	user_fn;
60
	bool		isnull;
61

62 63 64
	procedureTuple = SearchSysCache(PROCOID,
									ObjectIdGetDatum(functionId),
									0, 0, 0);
65
	if (!HeapTupleIsValid(procedureTuple))
66 67
		elog(ERROR, "fmgr_dynamic: function %u: cache lookup failed",
			 functionId);
68
	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
69

70
	proname = NameStr(procedureStruct->proname);
71 72 73

	prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
								 Anum_pg_proc_prosrc, &isnull);
74
	if (isnull)
75 76
		elog(ERROR, "fmgr: Could not extract prosrc for %u from pg_proc",
			 functionId);
77
	prosrcstring = DatumGetCString(DirectFunctionCall1(textout, prosrcattr));
78

79 80
	probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
								 Anum_pg_proc_probin, &isnull);
81
	if (isnull)
82 83
		elog(ERROR, "fmgr: Could not extract probin for %u from pg_proc",
			 functionId);
84
	probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));
85

86
	user_fn = load_external_function(probinstring, prosrcstring);
87

88
	pfree(prosrcstring);
89
	pfree(probinstring);
90

91 92
	ReleaseSysCache(procedureTuple);

93
	return user_fn;
94 95
}

96 97
PGFunction
load_external_function(char *filename, char *funcname)
98
{
99 100
	DynamicFileList *file_scanner;
	PGFunction	retval;
101 102
	char	   *load_error;
	struct stat stat_buf;
103 104

	/*
105
	 * Scan the list of loaded FILES to see if the file has been loaded.
106
	 */
107 108 109 110 111 112
	for (file_scanner = file_list;
		 file_scanner != (DynamicFileList *) NULL &&
			 strcmp(filename, file_scanner->filename) != 0;
		 file_scanner = file_scanner->next)
		;
	if (file_scanner == (DynamicFileList *) NULL)
113
	{
114 115 116 117 118 119
		/*
		 * Check for same files - different paths (ie, symlink or link)
		 */
		if (stat(filename, &stat_buf) == -1)
			elog(ERROR, "stat failed on file '%s': %m", filename);

120
		for (file_scanner = file_list;
121 122
			 file_scanner != (DynamicFileList *) NULL &&
				 !SAME_INODE(stat_buf, *file_scanner);
123 124 125 126 127 128
			 file_scanner = file_scanner->next)
			;
	}

	if (file_scanner == (DynamicFileList *) NULL)
	{
129 130 131 132 133 134
		/*
		 * File not loaded yet.
		 */
		file_scanner = (DynamicFileList *)
			malloc(sizeof(DynamicFileList) + strlen(filename));
		if (file_scanner == NULL)
135
			elog(ERROR, "Out of memory in load_external_function");
136

137
		MemSet((char *) file_scanner, 0, sizeof(DynamicFileList));
138 139 140 141 142 143 144 145 146 147
		strcpy(file_scanner->filename, filename);
		file_scanner->device = stat_buf.st_dev;
		file_scanner->inode = stat_buf.st_ino;
		file_scanner->next = (DynamicFileList *) NULL;

		file_scanner->handle = pg_dlopen(filename);
		if (file_scanner->handle == (void *) NULL)
		{
			load_error = (char *) pg_dlerror();
			free((char *) file_scanner);
148
			elog(ERROR, "Load of file %s failed: %s", filename, load_error);
149 150
		}

151 152 153 154 155
		/* OK to link it into list */
		if (file_list == (DynamicFileList *) NULL)
			file_list = file_scanner;
		else
			file_tail->next = file_scanner;
156 157 158
		file_tail = file_scanner;
	}

159 160 161 162 163 164 165
	/*
	 * If funcname is NULL, we only wanted to load the file.
	 */
	if (funcname == (char *) NULL)
		return (PGFunction) NULL;

	retval = pg_dlsym(file_scanner->handle, funcname);
166

167
	if (retval == (PGFunction) NULL)
168
		elog(ERROR, "Can't find function %s in file %s", funcname, filename);
169

170
	return retval;
171 172 173
}

/*
174 175 176
 * This function loads a shlib file without looking up any particular
 * function in it.  If the same shlib has previously been loaded,
 * unload and reload it.
177 178 179 180
 */
void
load_file(char *filename)
{
181
	DynamicFileList *file_scanner,
B
Bruce Momjian 已提交
182
			   *p;
183
	struct stat stat_buf;
184

185
	/*
B
Bruce Momjian 已提交
186 187 188
	 * We need to do stat() in order to determine whether this is the same
	 * file as a previously loaded file; it's also handy so as to give a
	 * good error message if bogus file name given.
189
	 */
190
	if (stat(filename, &stat_buf) == -1)
191
		elog(ERROR, "LOAD: could not open file '%s': %m", filename);
192

193
	if (file_list != (DynamicFileList *) NULL)
194
	{
195
		if (SAME_INODE(stat_buf, *file_list))
196
		{
197 198 199 200
			p = file_list;
			file_list = p->next;
			pg_dlclose(p->handle);
			free((char *) p);
201
		}
202
		else
203
		{
204 205 206 207 208 209 210 211 212 213 214 215 216
			for (file_scanner = file_list;
				 file_scanner->next != (DynamicFileList *) NULL;
				 file_scanner = file_scanner->next)
			{
				if (SAME_INODE(stat_buf, *(file_scanner->next)))
				{
					p = file_scanner->next;
					file_scanner->next = p->next;
					pg_dlclose(p->handle);
					free((char *) p);
					break;
				}
			}
217 218 219
		}
	}

220
	load_external_function(filename, (char *) NULL);
221
}