11/*-------------------------------------------------------------------------
22 *
33 * Utility routines for SQL dumping
4+ *Basically this is stuff that is useful in both pg_dump and pg_dumpall.
5+ *
46 *
57 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
68 * Portions Copyright (c) 1994, Regents of the University of California
79 *
8- *
9- * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.3 2002/09/07 16:14:33 petere Exp $
10+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.4 2003/05/30 22:55:15 tgl Exp $
1011 *
1112 *-------------------------------------------------------------------------
1213 */
1819#include "parser/keywords.h"
1920
2021
22+ static bool parseAclItem (const char * item ,const char * type ,const char * name ,
23+ int remoteVersion ,
24+ PQExpBuffer grantee ,PQExpBuffer grantor ,
25+ PQExpBuffer privs ,PQExpBuffer privswgo );
26+ static void AddAcl (PQExpBuffer aclbuf ,const char * keyword );
27+
2128
2229/*
2330 *Quotes input string if it's not a legitimate SQL identifier as-is.
@@ -89,7 +96,6 @@ fmtId(const char *rawid)
8996}
9097
9198
92-
9399/*
94100 * Convert a string value to an SQL string literal and append it to
95101 * the given buffer.
@@ -133,7 +139,9 @@ appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll)
133139}
134140
135141
136-
142+ /*
143+ * Convert backend's version string into a number.
144+ */
137145int
138146parse_version (const char * versionString )
139147{
@@ -152,3 +160,295 @@ parse_version(const char *versionString)
152160
153161return (100 * vmaj + vmin )* 100 + vrev ;
154162}
163+
164+
165+ /*
166+ * Build GRANT/REVOKE command(s) for an object.
167+ *
168+ *name: the object name, in the form to use in the commands (already quoted)
169+ *type: the object type (as seen in GRANT command: must be one of
170+ *TABLE, FUNCTION, LANGUAGE, or SCHEMA, or DATABASE)
171+ *acls: the ACL string fetched from the database
172+ *owner: username of object owner (will be passed through fmtId), or NULL
173+ *remoteVersion: version of database
174+ *
175+ * Returns TRUE if okay, FALSE if could not parse the acl string.
176+ * The resulting commands (if any) are appended to the contents of 'sql'.
177+ *
178+ * Note: beware of passing fmtId() result as 'name', since this routine
179+ * uses fmtId() internally.
180+ */
181+ bool
182+ buildACLCommands (const char * name ,const char * type ,
183+ const char * acls ,const char * owner ,
184+ int remoteVersion ,
185+ PQExpBuffer sql )
186+ {
187+ char * aclbuf ,
188+ * tok ;
189+ PQExpBuffer grantee ,grantor ,privs ,privswgo ;
190+ bool found_owner_privs = false;
191+
192+ if (strlen (acls )== 0 )
193+ return true;/* object has default permissions */
194+
195+ grantee = createPQExpBuffer ();
196+ grantor = createPQExpBuffer ();
197+ privs = createPQExpBuffer ();
198+ privswgo = createPQExpBuffer ();
199+
200+ /*
201+ * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
202+ * wire-in knowledge about the default public privileges for different
203+ * kinds of objects.
204+ */
205+ appendPQExpBuffer (sql ,"REVOKE ALL ON %s %s FROM PUBLIC;\n" ,
206+ type ,name );
207+
208+ /* Make a working copy of acls so we can use strtok */
209+ aclbuf = strdup (acls );
210+
211+ /* Scan comma-separated ACL items */
212+ for (tok = strtok (aclbuf ,"," );tok != NULL ;tok = strtok (NULL ,"," ))
213+ {
214+ size_t toklen ;
215+
216+ /*
217+ * Token may start with '{' and/or '"'. Actually only the start
218+ * of the string should have '{', but we don't verify that.
219+ */
220+ if (* tok == '{' )
221+ tok ++ ;
222+ if (* tok == '"' )
223+ tok ++ ;
224+ toklen = strlen (tok );
225+ while (toklen >=0 && (tok [toklen - 1 ]== '"' || tok [toklen - 1 ]== '}' ))
226+ tok [toklen -- - 1 ]= '\0' ;
227+
228+ if (!parseAclItem (tok ,type ,name ,remoteVersion ,
229+ grantee ,grantor ,privs ,privswgo ))
230+ return false;
231+
232+ if (grantor -> len == 0 && owner )
233+ printfPQExpBuffer (grantor ,"%s" ,owner );
234+
235+ if (privs -> len > 0 || privswgo -> len > 0 )
236+ {
237+ if (owner && strcmp (grantee -> data ,owner )== 0 )
238+ {
239+ /*
240+ * For the owner, the default privilege level is
241+ * ALL WITH GRANT OPTION.
242+ */
243+ found_owner_privs = true;
244+ if (strcmp (privswgo -> data ,"ALL" )!= 0 )
245+ {
246+ appendPQExpBuffer (sql ,"REVOKE ALL ON %s %s FROM %s;\n" ,
247+ type ,name ,
248+ fmtId (grantee -> data ));
249+ if (privs -> len > 0 )
250+ appendPQExpBuffer (sql ,"GRANT %s ON %s %s TO %s;\n" ,
251+ privs -> data ,type ,name ,
252+ fmtId (grantee -> data ));
253+ if (privswgo -> len > 0 )
254+ appendPQExpBuffer (sql ,"GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n" ,
255+ privswgo -> data ,type ,name ,
256+ fmtId (grantee -> data ));
257+ }
258+ }
259+ else
260+ {
261+ /*
262+ * Otherwise can assume we are starting from no privs.
263+ */
264+ if (privs -> len > 0 )
265+ {
266+ appendPQExpBuffer (sql ,"GRANT %s ON %s %s TO " ,
267+ privs -> data ,type ,name );
268+ if (grantee -> len == 0 )
269+ appendPQExpBuffer (sql ,"PUBLIC;\n" );
270+ else if (strncmp (grantee -> data ,"group " ,
271+ strlen ("group " ))== 0 )
272+ appendPQExpBuffer (sql ,"GROUP %s;\n" ,
273+ fmtId (grantee -> data + strlen ("group " )));
274+ else
275+ appendPQExpBuffer (sql ,"%s;\n" ,fmtId (grantee -> data ));
276+ }
277+ if (privswgo -> len > 0 )
278+ {
279+ appendPQExpBuffer (sql ,"GRANT %s ON %s %s TO " ,
280+ privswgo -> data ,type ,name );
281+ if (grantee -> len == 0 )
282+ appendPQExpBuffer (sql ,"PUBLIC" );
283+ else if (strncmp (grantee -> data ,"group " ,
284+ strlen ("group " ))== 0 )
285+ appendPQExpBuffer (sql ,"GROUP %s" ,
286+ fmtId (grantee -> data + strlen ("group " )));
287+ else
288+ appendPQExpBuffer (sql ,"%s" ,fmtId (grantee -> data ));
289+ appendPQExpBuffer (sql ," WITH GRANT OPTION;\n" );
290+ }
291+ }
292+ }
293+ else
294+ {
295+ /* No privileges. Issue explicit REVOKE for safety. */
296+ if (grantee -> len == 0 )
297+ ;/* Empty left-hand side means "PUBLIC"; already did it */
298+ else if (strncmp (grantee -> data ,"group " ,strlen ("group " ))== 0 )
299+ appendPQExpBuffer (sql ,"REVOKE ALL ON %s %s FROM GROUP %s;\n" ,
300+ type ,name ,
301+ fmtId (grantee -> data + strlen ("group " )));
302+ else
303+ appendPQExpBuffer (sql ,"REVOKE ALL ON %s %s FROM %s;\n" ,
304+ type ,name ,fmtId (grantee -> data ));
305+ }
306+ }
307+
308+ /*
309+ * If we didn't find any owner privs, the owner must have revoked 'em
310+ * all
311+ */
312+ if (!found_owner_privs && owner )
313+ {
314+ appendPQExpBuffer (sql ,"REVOKE ALL ON %s %s FROM %s;\n" ,
315+ type ,name ,fmtId (owner ));
316+ }
317+
318+ free (aclbuf );
319+ destroyPQExpBuffer (grantee );
320+ destroyPQExpBuffer (grantor );
321+ destroyPQExpBuffer (privs );
322+ destroyPQExpBuffer (privswgo );
323+
324+ return true;
325+ }
326+
327+
328+ /*
329+ * This will take an aclitem string of privilege code letters and
330+ * parse it into grantee, grantor, and privilege information. The
331+ * privilege information is split between privileges with grant option
332+ * (privswgo) and without (privs).
333+ *
334+ * Note: for cross-version compatibility, it's important to use ALL when
335+ * appropriate.
336+ */
337+ static bool
338+ parseAclItem (const char * item ,const char * type ,const char * name ,
339+ int remoteVersion ,
340+ PQExpBuffer grantee ,PQExpBuffer grantor ,
341+ PQExpBuffer privs ,PQExpBuffer privswgo )
342+ {
343+ char * buf ;
344+ bool all_with_go = true;
345+ bool all_without_go = true;
346+ char * eqpos ;
347+ char * slpos ;
348+ char * pos ;
349+
350+ buf = strdup (item );
351+
352+ /* user name is string up to = */
353+ eqpos = strchr (buf ,'=' );
354+ if (!eqpos )
355+ return false;
356+ * eqpos = '\0' ;
357+ printfPQExpBuffer (grantee ,"%s" ,buf );
358+
359+ /* grantor may be listed after / */
360+ slpos = strchr (eqpos + 1 ,'/' );
361+ if (slpos )
362+ {
363+ * slpos = '\0' ;
364+ printfPQExpBuffer (grantor ,"%s" ,slpos + 1 );
365+ }
366+ else
367+ resetPQExpBuffer (grantor );
368+
369+ /* privilege codes */
370+ #define CONVERT_PRIV (code ,keywd ) \
371+ if ((pos = strchr(eqpos + 1, code))) \
372+ { \
373+ if (*(pos + 1) == '*') \
374+ { \
375+ AddAcl(privswgo, keywd); \
376+ all_without_go = false; \
377+ } \
378+ else \
379+ { \
380+ AddAcl(privs, keywd); \
381+ all_with_go = false; \
382+ } \
383+ } \
384+ else \
385+ all_with_go = all_without_go = false
386+
387+ resetPQExpBuffer (privs );
388+ resetPQExpBuffer (privswgo );
389+
390+ if (strcmp (type ,"TABLE" )== 0 )
391+ {
392+ CONVERT_PRIV ('a' ,"INSERT" );
393+ CONVERT_PRIV ('r' ,"SELECT" );
394+ CONVERT_PRIV ('R' ,"RULE" );
395+
396+ if (remoteVersion >=70200 )
397+ {
398+ CONVERT_PRIV ('w' ,"UPDATE" );
399+ CONVERT_PRIV ('d' ,"DELETE" );
400+ CONVERT_PRIV ('x' ,"REFERENCES" );
401+ CONVERT_PRIV ('t' ,"TRIGGER" );
402+ }
403+ else
404+ {
405+ /* 7.0 and 7.1 have a simpler worldview */
406+ CONVERT_PRIV ('w' ,"UPDATE,DELETE" );
407+ }
408+ }
409+ else if (strcmp (type ,"FUNCTION" )== 0 )
410+ CONVERT_PRIV ('X' ,"EXECUTE" );
411+ else if (strcmp (type ,"LANGUAGE" )== 0 )
412+ CONVERT_PRIV ('U' ,"USAGE" );
413+ else if (strcmp (type ,"SCHEMA" )== 0 )
414+ {
415+ CONVERT_PRIV ('C' ,"CREATE" );
416+ CONVERT_PRIV ('U' ,"USAGE" );
417+ }
418+ else if (strcmp (type ,"DATABASE" )== 0 )
419+ {
420+ CONVERT_PRIV ('C' ,"CREATE" );
421+ CONVERT_PRIV ('T' ,"TEMPORARY" );
422+ }
423+ else
424+ abort ();
425+
426+ #undef CONVERT_PRIV
427+
428+ if (all_with_go )
429+ {
430+ resetPQExpBuffer (privs );
431+ printfPQExpBuffer (privswgo ,"ALL" );
432+ }
433+ else if (all_without_go )
434+ {
435+ resetPQExpBuffer (privswgo );
436+ printfPQExpBuffer (privs ,"ALL" );
437+ }
438+
439+ free (buf );
440+
441+ return true;
442+ }
443+
444+
445+ /*
446+ * Append a privilege keyword to a keyword list, inserting comma if needed.
447+ */
448+ static void
449+ AddAcl (PQExpBuffer aclbuf ,const char * keyword )
450+ {
451+ if (aclbuf -> len > 0 )
452+ appendPQExpBufferChar (aclbuf ,',' );
453+ appendPQExpBuffer (aclbuf ,"%s" ,keyword );
454+ }