1. #include "postgres.h"
2. #include "fmgr.h"
3. #include "funcapi.h"
4. #include "access/extprotocol.h"
5. #include "catalog/pg_proc.h"
6. #include "utils/array.h"
7. #include "utils/builtins.h"
8. #include "utils/memutils.h"
10. /* Our chosen URI format. We can change it however needed */
11. typedef struct DemoUri
12. {
13. char *protocol;
14. char *path;
15. } DemoUri;
16. static DemoUri *ParseDemoUri(const char *uri_str);
17. static void FreeDemoUri(DemoUri* uri);
19. /* Do the module magic dance */
20. PG_MODULE_MAGIC;
21. PG_FUNCTION_INFO_V1(demoprot_export);
22. PG_FUNCTION_INFO_V1(demoprot_import);
23. PG_FUNCTION_INFO_V1(demoprot_validate_urls);
25. Datum demoprot_export(PG_FUNCTION_ARGS);
26. Datum demoprot_import(PG_FUNCTION_ARGS);
27. Datum demoprot_validate_urls(PG_FUNCTION_ARGS);
29. /* A user context that persists across calls. Can be
30. declared in any other way */
31. typedef struct {
32. char *url;
33. char *filename;
34. FILE *file;
35. } extprotocol_t;
36. /*
37. * The read function - Import data into GPDB.
38. */
39. Datum
40. myprot_import(PG_FUNCTION_ARGS)
41. {
42. extprotocol_t *myData;
43. char *data;
44. int datlen;
45. size_t nread = 0;
47. /* Must be called via the external table format manager */
48. if (!CALLED_AS_EXTPROTOCOL(fcinfo))
49. elog(ERROR, "myprot_import: not called by external
50. protocol manager");
52. /* Get our internal description of the protocol */
53. myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
55. if(EXTPROTOCOL_IS_LAST_CALL(fcinfo))
56. {
57. /* we're done receiving data. close our connection */
58. if(myData && myData->file)
59. if(fclose(myData->file))
60. ereport(ERROR,
61. (errcode_for_file_access(),
62. errmsg("could not close file \"%s\": %m",
63. myData->filename)));
65. PG_RETURN_INT32(0);
66. }
68. if (myData == NULL)
69. {
70. /* first call. do any desired init */
72. const char *p_name = "myprot";
73. DemoUri *parsed_url;
74. char *url = EXTPROTOCOL_GET_URL(fcinfo);
75. myData = palloc(sizeof(extprotocol_t));
77. myData->url = pstrdup(url);
78. parsed_url = ParseDemoUri(myData->url);
79. myData->filename = pstrdup(parsed_url->path);
81. if(strcasecmp(parsed_url->protocol, p_name) != 0)
82. elog(ERROR, "internal error: myprot called with a
83. different protocol (%s)",
84. parsed_url->protocol);
86. FreeDemoUri(parsed_url);
88. /* open the destination file (or connect to remote server in
89. other cases) */
90. myData->file = fopen(myData->filename, "r");
92. if (myData->file == NULL)
93. ereport(ERROR,
94. (errcode_for_file_access(),
95. errmsg("myprot_import: could not open file \"%s\"
96. for reading: %m",
97. myData->filename),
98. errOmitLocation(true)));
100. EXTPROTOCOL_SET_USER_CTX(fcinfo, myData);
101. }
102. /* ==========================================
103. * DO THE IMPORT
104. * ========================================== */
105. data = EXTPROTOCOL_GET_DATABUF(fcinfo);
106. datlen = EXTPROTOCOL_GET_DATALEN(fcinfo);
108. /* read some bytes (with fread in this example, but normally
109. in some other method over the network) */
110. if(datlen > 0)
111. {
112. nread = fread(data, 1, datlen, myData->file);
113. if (ferror(myData->file))
114. ereport(ERROR,
115. (errcode_for_file_access(),
116. errmsg("myprot_import: could not write to file
117. \"%s\": %m",
118. myData->filename)));
119. }
120. PG_RETURN_INT32((int)nread);
121. }
122. /*
123. * Write function - Export data out of GPDB
124. */
125. Datum
126. myprot_export(PG_FUNCTION_ARGS)
127. {
128. extprotocol_t *myData;
129. char *data;
130. int datlen;
131. size_t wrote = 0;
133. /* Must be called via the external table format manager */
134. if (!CALLED_AS_EXTPROTOCOL(fcinfo))
135. elog(ERROR, "myprot_export: not called by external
136. protocol manager");
138. /* Get our internal description of the protocol */
139. myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
140. if(EXTPROTOCOL_IS_LAST_CALL(fcinfo))
141. {
142. /* we're done sending data. close our connection */
143. if(myData && myData->file)
144. if(fclose(myData->file))
145. ereport(ERROR,
146. (errcode_for_file_access(),
147. errmsg("could not close file \"%s\": %m",
148. myData->filename)));
150. PG_RETURN_INT32(0);
151. }
152. if (myData == NULL)
153. {
154. /* first call. do any desired init */
155. const char *p_name = "myprot";
156. DemoUri *parsed_url;
157. char *url = EXTPROTOCOL_GET_URL(fcinfo);
159. myData = palloc(sizeof(extprotocol_t));
161. myData->url = pstrdup(url);
162. parsed_url = ParseDemoUri(myData->url);
163. myData->filename = pstrdup(parsed_url->path);
165. if(strcasecmp(parsed_url->protocol, p_name) != 0)
166. elog(ERROR, "internal error: myprot called with a
167. different protocol (%s)",
168. parsed_url->protocol);
170. FreeDemoUri(parsed_url);
172. /* open the destination file (or connect to remote server in
173. other cases) */
174. myData->file = fopen(myData->filename, "a");
175. if (myData->file == NULL)
176. ereport(ERROR,
177. (errcode_for_file_access(),
178. errmsg("myprot_export: could not open file \"%s\"
179. for writing: %m",
180. myData->filename),
181. errOmitLocation(true)));
183. EXTPROTOCOL_SET_USER_CTX(fcinfo, myData);
184. }
185. /* ========================================
186. * DO THE EXPORT
187. * ======================================== */
188. data = EXTPROTOCOL_GET_DATABUF(fcinfo);
189. datlen = EXTPROTOCOL_GET_DATALEN(fcinfo);
191. if(datlen > 0)
192. {
193. wrote = fwrite(data, 1, datlen, myData->file);
195. if (ferror(myData->file))
196. ereport(ERROR,
197. (errcode_for_file_access(),
198. errmsg("myprot_import: could not read from file
199. \"%s\": %m",
200. myData->filename)));
201. }
202. PG_RETURN_INT32((int)wrote);
203. }
204. Datum
205. myprot_validate_urls(PG_FUNCTION_ARGS)
206. {
207. List *urls;
208. int nurls;
209. int i;
210. ValidatorDirection direction;
212. /* Must be called via the external table format manager */
213. if (!CALLED_AS_EXTPROTOCOL_VALIDATOR(fcinfo))
214. elog(ERROR, "myprot_validate_urls: not called by external
215. protocol manager");
217. nurls = EXTPROTOCOL_VALIDATOR_GET_NUM_URLS(fcinfo);
218. urls = EXTPROTOCOL_VALIDATOR_GET_URL_LIST(fcinfo);
219. direction = EXTPROTOCOL_VALIDATOR_GET_DIRECTION(fcinfo);
220. /*
221. * Dumb example 1: search each url for a substring
222. * we don't want to be used in a url. in this example
223. * it's 'secured_directory'.
224. */
225. for (i = 1 ; i <= nurls ; i++)
226. {
227. char *url = EXTPROTOCOL_VALIDATOR_GET_NTH_URL(fcinfo, i);
229. if (strstr(url, "secured_directory") != 0)
230. {
231. ereport(ERROR,
232. (errcode(ERRCODE_PROTOCOL_VIOLATION),
233. errmsg("using 'secured_directory' in a url
234. isn't allowed ")));
235. }
236. }
237. /*
238. * Dumb example 2: set a limit on the number of urls
239. * used. In this example we limit readable external
240. * tables that use our protocol to 2 urls max.
241. */
242. if(direction == EXT_VALIDATE_READ && nurls > 2)
243. {
244. ereport(ERROR,
245. (errcode(ERRCODE_PROTOCOL_VIOLATION),
246. errmsg("more than 2 urls aren't allowed in this protocol ")));
247. }
248. PG_RETURN_VOID();
249. }
250. /* --- utility functions --- */
251. static
252. DemoUri *ParseDemoUri(const char *uri_str)
253. {
254. DemoUri *uri = (DemoUri *) palloc0(sizeof(DemoUri));
255. int protocol_len;
257. uri->path = NULL;
258. uri->protocol = NULL;
259. /*
260. * parse protocol
261. */
262. char *post_protocol = strstr(uri_str, "://");
264. if(!post_protocol)
265. {
266. ereport(ERROR,
267. (errcode(ERRCODE_SYNTAX_ERROR),
268. errmsg("invalid protocol URI \'%s\'", uri_str),
269. errOmitLocation(true)));
270. }
272. protocol_len = post_protocol - uri_str;
273. uri->protocol = (char *)palloc0(protocol_len + 1);
274. strncpy(uri->protocol, uri_str, protocol_len);
276. /* make sure there is more to the uri string */
277. if (strlen(uri_str) <= protocol_len)
278. ereport(ERROR,
279. (errcode(ERRCODE_SYNTAX_ERROR),
280. errmsg("invalid myprot URI \'%s\' : missing path",
281. uri_str),
282. errOmitLocation(true)));
284. /* parse path */
285. uri->path = pstrdup(uri_str + protocol_len + strlen("://"));
287. return uri;
288. }
289. static
290. void FreeDemoUri(DemoUri *uri)
291. {
292. if (uri->path)
293. pfree(uri->path);
294. if (uri->protocol)
295. pfree(uri->protocol);
297. pfree(uri);
298. }
Parent topic: Installing the External Table Protocol
