CVS User Account cvsuser
Wed Mar 30 16:24:17 PST 2005
Log Message:
-----------
First step of implementing slon_quote_ident. This adds code taken from PostgreSQL -HEAD. Our function now quotes all identifiers known by your version of PostgreSQL.

Modified Files:
--------------
    slony1-engine/src/backend:
        slony1_funcs.c (r1.28 -> r1.29)

-------------- next part --------------
Index: slony1_funcs.c
===================================================================
RCS file: /usr/local/cvsroot/slony1/slony1-engine/src/backend/slony1_funcs.c,v
retrieving revision 1.28
retrieving revision 1.29
diff -Lsrc/backend/slony1_funcs.c -Lsrc/backend/slony1_funcs.c -u -w -r1.28 -r1.29
--- src/backend/slony1_funcs.c
+++ src/backend/slony1_funcs.c
@@ -3,7 +3,7 @@
  *
  *	  The C functions and triggers portion of Slony-I.
  *
- *	Copyright (c) 2003-2004, PostgreSQL Global Development Group
+ *	Copyright (c) 2003-2005, PostgreSQL Global Development Group
  *	Author: Jan Wieck, Afilias USA INC.
  *
  *	$Id$
@@ -46,6 +46,9 @@
 PG_FUNCTION_INFO_V1(_Slony_I_terminateNodeConnections);
 PG_FUNCTION_INFO_V1(_Slony_I_cleanupListener);
 
+PG_FUNCTION_INFO_V1(_slon_quote_ident);
+
+
 Datum		_Slony_I_createEvent(PG_FUNCTION_ARGS);
 Datum		_Slony_I_getLocalNodeId(PG_FUNCTION_ARGS);
 Datum		_Slony_I_getModuleVersion(PG_FUNCTION_ARGS);
@@ -58,6 +61,9 @@
 Datum		_Slony_I_terminateNodeConnections(PG_FUNCTION_ARGS);
 Datum		_Slony_I_cleanupListener(PG_FUNCTION_ARGS);
 
+Datum		_slon_quote_ident(PG_FUNCTION_ARGS);
+
+
 #ifdef CYGWIN
 extern DLLIMPORT Node *newNodeMacroHolder;
 #endif
@@ -110,6 +116,7 @@
 static Slony_I_ClusterStatus *
 getClusterStatus(Name cluster_name,
 				 int need_plan_mask);
+const char * slon_quote_identifier(const char *ident);
 static char *slon_quote_literal(char *str);
 
 
@@ -501,7 +508,7 @@
 			if ((col_value[i] = SPI_getvalue(new_row, tupdesc, i + 1)) == NULL)
 				continue;
 
-			col_ident = (char *)quote_identifier(SPI_fname(tupdesc, i + 1));
+			col_ident = (char *)slon_quote_identifier(SPI_fname(tupdesc, i + 1));
 			col_value[i] = slon_quote_literal(col_value[i]);
 
 			cmddata_need = (cp - (char *)(cs->cmddata_buf)) + 16 +
@@ -688,7 +695,7 @@
 			else
 				need_comma = true;
 
-			col_ident = (char *)quote_identifier(SPI_fname(tupdesc, i + 1));
+			col_ident = (char *)slon_quote_identifier(SPI_fname(tupdesc, i + 1));
 			if (new_isnull)
 				col_value = "NULL";
 			else
@@ -732,7 +739,7 @@
 				if (attkind[attkind_idx] == 'k')
 					break;
 			}
-			col_ident = (char *)quote_identifier(SPI_fname(tupdesc, i + 1));
+			col_ident = (char *)slon_quote_identifier(SPI_fname(tupdesc, i + 1));
 			col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1));
 
 			cmddata_need = (cp - (char *)(cs->cmddata_buf)) + 16 +
@@ -774,7 +781,7 @@
 			attkind_idx++;
 			if (attkind[attkind_idx] != 'k')
 				continue;
-			col_ident = (char *)quote_identifier(SPI_fname(tupdesc, i + 1));
+			col_ident = (char *)slon_quote_identifier(SPI_fname(tupdesc, i + 1));
 			col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1));
 
 			cmddata_need = (cp - (char *)(cs->cmddata_buf)) + 16 +
@@ -838,7 +845,7 @@
 			attkind_idx++;
 			if (attkind[attkind_idx] != 'k')
 				continue;
-			col_ident = (char *)quote_identifier(SPI_fname(tupdesc, i + 1));
+			col_ident = (char *)slon_quote_identifier(SPI_fname(tupdesc, i + 1));
 			col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1));
 
 			cmddata_need = (cp - (char *)(cs->cmddata_buf)) + 16 +
@@ -1097,7 +1104,7 @@
 	cp2 = result;
 
 	*cp2++ = '\'';
-	while (len > 0)
+	while (len-- > 0)
 	{
 		if ((wl = pg_mblen(cp1)) != 1)
 		{
@@ -1113,8 +1120,6 @@
 		if (*cp1 == '\\')
 			*cp2++ = '\\';
 		*cp2++ = *cp1++;
-
-		len--;
 	}
 
 	*cp2++ = '\'';
@@ -1124,6 +1129,124 @@
 }
 
 
+/*
+ * slon_quote_identifier                     - Quote an identifier only if needed
+ *
+ * When quotes are needed, we palloc the required space; slightly
+ * space-wasteful but well worth it for notational simplicity.
+ *
+ * Version: pgsql/src/backend/utils/adt/ruleutils.c,v 1.188 2005/01/13 17:19:10
+ */
+const char *
+slon_quote_identifier(const char *ident)
+{
+        /*
+         * Can avoid quoting if ident starts with a lowercase letter or
+         * underscore and contains only lowercase letters, digits, and
+         * underscores, *and* is not any SQL keyword.  Otherwise, supply
+         * quotes.
+         */
+        int                     nquotes = 0;
+        bool            safe;
+        const char *ptr;
+        char       *result;
+        char       *optr;
+
+        /*
+         * would like to use <ctype.h> macros here, but they might yield
+         * unwanted locale-specific results...
+         */
+        safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
+
+        for (ptr = ident; *ptr; ptr++)
+        {
+                char            ch = *ptr;
+
+                if ((ch >= 'a' && ch <= 'z') ||
+                        (ch >= '0' && ch <= '9') ||
+                        (ch == '_'))
+                {
+                        /* okay */
+                }
+                else
+                {
+                        safe = false;
+                        if (ch == '"')
+                                nquotes++;
+                }
+        }
+
+        if (safe)
+        {
+                /*
+                 * Check for keyword.  This test is overly strong, since many of
+                 * the "keywords" known to the parser are usable as column names,
+                 * but the parser doesn't provide any easy way to test for whether
+                 * an identifier is safe or not... so be safe not sorry.
+                 *
+                 * Note: ScanKeywordLookup() does case-insensitive comparison, but
+                 * that's fine, since we already know we have all-lower-case.
+                 */
+                if (ScanKeywordLookup(ident) != NULL)
+                        safe = false;
+        }
+
+        if (safe)
+                return ident;                   /* no change needed */
+
+        result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
+
+        optr = result;
+        *optr++ = '"';
+        for (ptr = ident; *ptr; ptr++)
+        {
+                char            ch = *ptr;
+
+                if (ch == '"')
+                        *optr++ = '"';
+                *optr++ = ch;
+        }
+        *optr++ = '"';
+        *optr = '\0';
+
+        return result;
+}
+
+
+
+/*
+ * _slon_quote_ident -
+ *        returns a properly quoted identifier
+ *
+ * Version: pgsql/src/backend/utils/adt/quote.c,v 1.14.4.1 2005/03/21 16:29:31
+ */
+Datum
+_slon_quote_ident(PG_FUNCTION_ARGS)
+{
+        text       *t = PG_GETARG_TEXT_P(0);
+        text       *result;
+        const char *qstr;
+        char       *str;
+        int                     len;
+
+        /* We have to convert to a C string to use quote_identifier */
+        len = VARSIZE(t) - VARHDRSZ;
+        str = (char *) palloc(len + 1);
+        memcpy(str, VARDATA(t), len);
+        str[len] = '\0';
+
+        qstr = slon_quote_identifier(str);
+
+        len = strlen(qstr);
+        result = (text *) palloc(len + VARHDRSZ);
+        VARATT_SIZEP(result) = len + VARHDRSZ;
+        memcpy(VARDATA(result), qstr, len);
+
+        PG_RETURN_TEXT_P(result);
+}
+
+
+
 static Slony_I_ClusterStatus *
 getClusterStatus(Name cluster_name, int need_plan_mask)
 {


More information about the Slony1-commit mailing list