File: hstore_pllua.c

package info (click to toggle)
postgresql-pllua 1%3A2.0.10-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,316 kB
  • sloc: ansic: 14,369; sql: 2,181; makefile: 163; sh: 59; javascript: 38
file content (274 lines) | stat: -rw-r--r-- 6,877 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/* hstore_pllua.c */

/* note, we do not support out-of-pllua-tree building */
#include "pllua.h"

#include "hstore/hstore.h"

#include "mb/pg_wchar.h"

PG_MODULE_MAGIC;

#ifndef PG_GETARG_HSTORE_P
#define PG_GETARG_HSTORE_P(h_) PG_GETARG_HS(h_)
#endif

extern void _PG_init(void);

/* Linkage to functions in hstore module */
typedef HStore *(*hstoreUpgrade_t) (Datum orig);
static hstoreUpgrade_t hstoreUpgrade_p;
typedef int (*hstoreUniquePairs_t) (Pairs *a, int32 l, int32 *buflen);
static hstoreUniquePairs_t hstoreUniquePairs_p;
typedef HStore *(*hstorePairs_t) (Pairs *pairs, int32 pcount, int32 buflen);
static hstorePairs_t hstorePairs_p;
typedef size_t (*hstoreCheckKeyLen_t) (size_t len);
static hstoreCheckKeyLen_t hstoreCheckKeyLen_p;
typedef size_t (*hstoreCheckValLen_t) (size_t len);
static hstoreCheckValLen_t hstoreCheckValLen_p;

/* Linkage to functions in pllua module */
typedef void (*pllua_pcall_t)(lua_State *L, int nargs, int nresults, int msgh);
static pllua_pcall_t pllua_pcall_p;
typedef lua_CFunction pllua_trampoline_t;
static lua_CFunction pllua_trampoline_p;
typedef bool (*pllua_pairs_start_t) (lua_State *L, int nd, bool noerror);
static pllua_pairs_start_t pllua_pairs_start_p;
typedef int (*pllua_pairs_next_t) (lua_State *L);
static pllua_pairs_next_t pllua_pairs_next_p;

/*
 * Module initialize function: fetch function pointers for cross-module calls.
 */
void
_PG_init(void)
{
#define EXTFUNCS(x_) #x_
#define EXTFUNCT(xp_) xp_##_t
#define EXTFUNCP(xp_) xp_##_p
#define EXTFUNC(lib_, n_)									\
	AssertVariableIsOfType(&n_, EXTFUNCT(n_));				\
	EXTFUNCP(n_) = (EXTFUNCT(n_))							\
		load_external_function(lib_, EXTFUNCS(n_), true, NULL);

	EXTFUNC("$libdir/hstore", hstoreUpgrade);
	EXTFUNC("$libdir/hstore", hstoreUniquePairs);
	EXTFUNC("$libdir/hstore", hstorePairs);
	EXTFUNC("$libdir/hstore", hstoreCheckKeyLen);
	EXTFUNC("$libdir/hstore", hstoreCheckValLen);

	EXTFUNC("$libdir/pllua", pllua_pcall);
	EXTFUNC("$libdir/pllua", pllua_trampoline);
	EXTFUNC("$libdir/pllua", pllua_pairs_start);
	EXTFUNC("$libdir/pllua", pllua_pairs_next);
}


/* These defines must be after the module init function */
#define hstoreUpgrade hstoreUpgrade_p
#define hstoreUniquePairs hstoreUniquePairs_p
#define hstorePairs hstorePairs_p
#define hstoreCheckKeyLen hstoreCheckKeyLen_p
#define hstoreCheckValLen hstoreCheckValLen_p

#define pllua_pcall pllua_pcall_p
#define pllua_trampoline pllua_trampoline_p
#define pllua_pairs_start pllua_pairs_start_p
#define pllua_pairs_next pllua_pairs_next_p


static int
hstore_to_pllua_real(lua_State *L)
{
	HStore	   *in = lua_touserdata(L, 1);
	int			i;
	int			count = HS_COUNT(in);
	char	   *base = STRPTR(in);
	HEntry	   *entries = ARRPTR(in);

	lua_createtable(L, 0, count);

	for (i = 0; i < count; i++)
	{
		lua_pushlstring(L,
						HSTORE_KEY(entries, base, i),
						HSTORE_KEYLEN(entries, i));
		if (HSTORE_VALISNULL(entries, i))
			lua_pushboolean(L, 0);
		else
			lua_pushlstring(L,
						   HSTORE_VAL(entries, base, i),
						   HSTORE_VALLEN(entries, i));
		lua_rawset(L, -3);
	}

	return 1;
}

/*
 * equivalent to:
 *
 *  local keys,vals = {},{}
 *  for k,v in pairs(hs) do keys[#keys+1] = k vals[#vals+1] = v end
 *  then makes a full userdata with a Pairs array and refs to keys,vals
 *
 */
static int
pllua_to_hstore_real(lua_State *L)
{
	Pairs	   *pairs = NULL;
	int			idx = 0;
	int			pcount = 0;
	bool		metaloop;

	/*
	 * Decline if there isn't exactly 1 arg.
	 */
	if (lua_gettop(L) != 1)
	{
		lua_pushnil(L);
		lua_pushnil(L);
		return 2;
	}

	lua_newtable(L); /* index 2: keys */
	lua_newtable(L); /* index 3: vals */

	metaloop = pllua_pairs_start(L, 1, true);

	/*
	 * If it doesn't have a pairs metamethod and it's not a plain table,
	 * then we have to decline it.
	 */
	if (!metaloop && !lua_istable(L, 1))
	{
		/* pairs_start already pushed one nil */
		lua_pushnil(L);
		return 2;
	}

	while (metaloop ? pllua_pairs_next(L) : lua_next(L, 1))
	{
		++idx;
		if (lua_isnil(L, -1) || (lua_isboolean(L, -1) && !lua_toboolean(L, -1)))
		{
			lua_pop(L, 1);
		}
		else
		{
			luaL_tolstring(L, -1, NULL);
			lua_rawseti(L, 3, idx);
			lua_pop(L, 1);
		}
		luaL_tolstring(L, -1, NULL);
		lua_rawseti(L, 2, idx);
	}

	lua_settop(L, 3);
	pcount = idx;
	lua_pushinteger(L, pcount);  /* first result */
	pairs = lua_newuserdata(L, (idx ? idx : 1) * sizeof(Pairs));
	lua_newtable(L);
	lua_pushvalue(L, 2);
	lua_setfield(L, -2, "keys");
	lua_pushvalue(L, 3);
	lua_setfield(L, -2, "values");
	lua_setuservalue(L, -2);
	for (idx = 0; idx < pcount; ++idx)
	{
		lua_rawgeti(L, 2, idx+1);
		pairs[idx].key = (char *) lua_tolstring(L, -1, &(pairs[idx].keylen));
		pairs[idx].needfree = false;
		lua_pop(L, 1);

		if (lua_rawgeti(L, 3, idx+1) == LUA_TNIL)
		{
			pairs[idx].val = NULL;
			pairs[idx].vallen = 0;
			pairs[idx].isnull = true;
		}
		else
		{
			pairs[idx].val = (char *) lua_tolstring(L, -1, &(pairs[idx].vallen));
			pairs[idx].isnull = false;
		}
		lua_pop(L, 1);
	}
	return 2;
}


PG_FUNCTION_INFO_V1(hstore_to_pllua);

Datum
hstore_to_pllua(PG_FUNCTION_ARGS)
{
	HStore	   *in = PG_GETARG_HSTORE_P(0);
	pllua_node *node = (pllua_node *) fcinfo->context;
	lua_State  *L;

	if (!node || node->type != T_Invalid || node->magic != PLLUA_MAGIC)
		elog(ERROR, "hstore_to_pllua must only be called from pllua");

	L = node->L;
	pllua_pushcfunction(L, pllua_trampoline);
	lua_pushlightuserdata(L, hstore_to_pllua_real);
	lua_pushlightuserdata(L, in);
	pllua_pcall(L, 2, 1, 0);

	return (Datum)0;
}


PG_FUNCTION_INFO_V1(pllua_to_hstore);

Datum
pllua_to_hstore(PG_FUNCTION_ARGS)
{
	pllua_node *node = (pllua_node *) fcinfo->context;
	lua_State  *L;
	int32		pcount = 0;
	HStore	   *out = NULL;
	Pairs	   *pairs;

	if (!node || node->type != T_Invalid || node->magic != PLLUA_MAGIC)
		elog(ERROR, "pllua_to_hstore must only be called from pllua");

	L = node->L;
	pllua_pushcfunction(L, pllua_trampoline);
	lua_insert(L, 1);
	lua_pushlightuserdata(L, pllua_to_hstore_real);
	lua_insert(L, 2);
	pllua_pcall(L, lua_gettop(L) - 1, 2, 0);

	/*
	 * this ptr is the Pairs struct as a Lua full userdata, which carries
	 * refs to the tables holding the keys and values strings to prevent
	 * them being GC'd. hstorePairs will copy everything into a new palloc'd
	 * value, and the storage will be GC'd sometime later after we pop it.
	 */
	pcount = lua_tointeger(L, -2);
	pairs = lua_touserdata(L, -1);

	if (pairs)
	{
		int i;
		int32 buflen;
		for (i = 0; i < pcount; ++i)
		{
			pairs[i].keylen = hstoreCheckKeyLen(pairs[i].keylen);
			pairs[i].vallen = hstoreCheckKeyLen(pairs[i].vallen);
			pg_verifymbstr(pairs[i].key, pairs[i].keylen, false);
			pg_verifymbstr(pairs[i].val, pairs[i].vallen, false);
		}
		pcount = hstoreUniquePairs(pairs, pcount, &buflen);
		out = hstorePairs(pairs, pcount, buflen);
	}

	lua_pop(L, 2);

	if (out)
		PG_RETURN_POINTER(out);
	else
		PG_RETURN_NULL();
}