1
1
/*-------------------------------------------------------------------------
2
2
*
3
3
* Utility routines for SQL dumping
4
+ *Basically this is stuff that is useful in both pg_dump and pg_dumpall.
5
+ *
4
6
*
5
7
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
6
8
* Portions Copyright (c) 1994, Regents of the University of California
7
9
*
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 $
10
11
*
11
12
*-------------------------------------------------------------------------
12
13
*/
18
19
#include "parser/keywords.h"
19
20
20
21
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
+
21
28
22
29
/*
23
30
*Quotes input string if it's not a legitimate SQL identifier as-is.
@@ -89,7 +96,6 @@ fmtId(const char *rawid)
89
96
}
90
97
91
98
92
-
93
99
/*
94
100
* Convert a string value to an SQL string literal and append it to
95
101
* the given buffer.
@@ -133,7 +139,9 @@ appendStringLiteral(PQExpBuffer buf, const char *str, bool escapeAll)
133
139
}
134
140
135
141
136
-
142
+ /*
143
+ * Convert backend's version string into a number.
144
+ */
137
145
int
138
146
parse_version (const char * versionString )
139
147
{
@@ -152,3 +160,295 @@ parse_version(const char *versionString)
152
160
153
161
return (100 * vmaj + vmin )* 100 + vrev ;
154
162
}
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
+ }