blob: 6b6a3c58b3ee9f6f1c0f773ef56cfc992947d4ee [file] [log] [blame]
#include "postgres.h"
#include "funcapi.h"
#include "assert.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "catalog/namespace.h"
#include "ctype.h"
#include "string.h"
#include "orafunc.h"
#include "builtins.h"
PG_FUNCTION_INFO_V1(dbms_assert_enquote_literal);
PG_FUNCTION_INFO_V1(dbms_assert_enquote_name);
PG_FUNCTION_INFO_V1(dbms_assert_noop);
PG_FUNCTION_INFO_V1(dbms_assert_qualified_sql_name);
PG_FUNCTION_INFO_V1(dbms_assert_schema_name);
PG_FUNCTION_INFO_V1(dbms_assert_simple_sql_name);
PG_FUNCTION_INFO_V1(dbms_assert_object_name);
#define CUSTOM_EXCEPTION(code, msg) \
ereport(ERROR, \
(errcode(ERRCODE_ORA_PACKAGES_##code), \
errmsg(msg)))
#define INVALID_SCHEMA_NAME_EXCEPTION() \
CUSTOM_EXCEPTION(INVALID_SCHEMA_NAME, "invalid schema name")
#define INVALID_OBJECT_NAME_EXCEPTION() \
CUSTOM_EXCEPTION(INVALID_OBJECT_NAME, "invalid object name")
#define ISNOT_SIMPLE_SQL_NAME_EXCEPTION() \
CUSTOM_EXCEPTION(ISNOT_SIMPLE_SQL_NAME, "string is not simple SQL name")
#define ISNOT_QUALIFIED_SQL_NAME_EXCEPTION() \
CUSTOM_EXCEPTION(ISNOT_QUALIFIED_SQL_NAME, "string is not qualified SQL name")
#define EMPTY_STR(str) ((VARSIZE(str) - VARHDRSZ) == 0)
static bool check_sql_name(char *cp, int len);
static bool ParseIdentifierString(char *rawstring);
/*
* Procedure ParseIdentifierString is based on SplitIdentifierString
* from varlena.c. We need different behave of quote symbol evaluation.
*/
bool
ParseIdentifierString(char *rawstring)
{
char *nextp = rawstring;
bool done = false;
while (isspace((unsigned char) *nextp))
nextp++; /* skip leading whitespace */
if (*nextp == '\0')
return true; /* allow empty string */
/* At the top of the loop, we are at start of a new identifier. */
do
{
char *curname;
char *endp;
if (*nextp == '\"')
{
/* Quoted name --- collapse quote-quote pairs, no downcasing */
curname = nextp + 1;
for (;;)
{
endp = strchr(nextp + 1, '\"');
if (endp == NULL)
return false; /* mismatched quotes */
if (endp[1] != '\"')
break; /* found end of quoted name */
/* Collapse adjacent quotes into one quote, and look again */
memmove(endp, endp + 1, strlen(endp));
nextp = endp;
}
/* endp now points at the terminating quote */
nextp = endp + 1;
}
else
{
/* Unquoted name --- extends to separator or whitespace */
curname = nextp;
while (*nextp && *nextp != '.' &&
!isspace((unsigned char) *nextp))
{
if (!isalnum(*nextp) && *nextp != '_')
return false;
nextp++;
}
endp = nextp;
if (curname == nextp)
return false; /* empty unquoted name not allowed */
}
while (isspace((unsigned char) *nextp))
nextp++; /* skip trailing whitespace */
if (*nextp == '.')
{
nextp++;
while (isspace((unsigned char) *nextp))
nextp++; /* skip leading whitespace for next */
/* we expect another name, so done remains false */
}
else if (*nextp == '\0')
done = true;
else
return false; /* invalid syntax */
/* Loop back if we didn't reach end of string */
} while (!done);
return true;
}
/****************************************************************
* DBMS_ASSERT.ENQUOTE_LITERAL
*
* Syntax:
* FUNCTION ENQUOTE_LITERAL(str varchar) RETURNS varchar;
*
* Purpouse:
* Add leading and trailing quotes, verify that all single quotes
* are paired with adjacent single quotes.
*
****************************************************************/
Datum
dbms_assert_enquote_literal(PG_FUNCTION_ARGS)
{
PG_RETURN_DATUM(DirectFunctionCall1(quote_literal, PG_GETARG_DATUM(0)));
}
/****************************************************************
* DBMS_ASSERT.ENQUOTE_NAME
*
* Syntax:
* FUNCTION ENQUOTE_NAME(str varchar) RETURNS varchar;
* FUNCTION ENQUOTE_NAME(str varchar, loweralize boolean := true)
* RETURNS varchar;
* Purpouse:
* Enclose name in double quotes.
* Atention!:
* On Oracle is second parameter capitalize!
*
****************************************************************/
Datum
dbms_assert_enquote_name(PG_FUNCTION_ARGS)
{
Datum name = PG_GETARG_DATUM(0);
bool loweralize = PG_GETARG_BOOL(1);
name = DirectFunctionCall1(quote_ident, name);
if (loweralize)
name = DirectFunctionCall1(lower, name);
PG_RETURN_DATUM(name);
}
/****************************************************************
* DBMS_ASSERT.NOOP
*
* Syntax:
* FUNCTION NOOP(str varchar) RETURNS varchar;
*
* Purpouse:
* Returns value without any checking.
*
****************************************************************/
Datum
dbms_assert_noop(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_P(0);
PG_RETURN_TEXT_P(TextPCopy(str));
}
/****************************************************************
* DBMS_ASSERT.QUALIFIED_SQL_NAME
*
* Syntax:
* FUNCTION QUALIFIED_SQL_NAME(str varchar) RETURNS varchar;
*
* Purpouse:
* This function verifies that the input string is qualified SQL
* name.
* Exception: 44004 string is not a qualified SQL name
*
****************************************************************/
Datum
dbms_assert_qualified_sql_name(PG_FUNCTION_ARGS)
{
text *qname;
if (PG_ARGISNULL(0))
ISNOT_QUALIFIED_SQL_NAME_EXCEPTION();
qname = PG_GETARG_TEXT_P(0);
if (EMPTY_STR(qname))
ISNOT_QUALIFIED_SQL_NAME_EXCEPTION();
if (!ParseIdentifierString(text_to_cstring(qname)))
ISNOT_QUALIFIED_SQL_NAME_EXCEPTION();
PG_RETURN_TEXT_P(qname);
}
/****************************************************************
* DBMS_ASSERT.SCHEMA_NAME
*
* Syntax:
* FUNCTION SCHEMA_NAME(str varchar) RETURNS varchar;
*
* Purpouse:
* Function verifies that input string is an existing schema
* name.
* Exception: 44001 Invalid schema name
*
****************************************************************/
Datum
dbms_assert_schema_name(PG_FUNCTION_ARGS)
{
Oid namespaceId;
AclResult aclresult;
text *sname;
char *nspname;
List *names;
if (PG_ARGISNULL(0))
INVALID_SCHEMA_NAME_EXCEPTION();
sname = PG_GETARG_TEXT_P(0);
if (EMPTY_STR(sname))
INVALID_SCHEMA_NAME_EXCEPTION();
nspname = text_to_cstring(sname);
#ifdef GP_VERSION_NUM
names = stringToQualifiedNameList(nspname, "dbms");
#else
names = stringToQualifiedNameList(nspname);
#endif
if (list_length(names) != 1)
INVALID_SCHEMA_NAME_EXCEPTION();
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(strVal(linitial(names))),
0, 0, 0);
if (!OidIsValid(namespaceId))
INVALID_SCHEMA_NAME_EXCEPTION();
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
INVALID_SCHEMA_NAME_EXCEPTION();
PG_RETURN_TEXT_P(sname);
}
/****************************************************************
* DBMS_ASSERT.SIMPLE_SQL_NAME
*
* Syntax:
* FUNCTION SIMPLE_SQL_NAME(str varchar) RETURNS varchar;
*
* Purpouse:
* This function verifies that the input string is simple SQL
* name.
* Exception: 44003 String is not a simple SQL name
*
****************************************************************/
static bool
check_sql_name(char *cp, int len)
{
if (*cp == '"')
{
for (cp++, len -= 2; len-- > 0; cp++)
{
/* all double quotes have to be paired */
if (*cp == '"')
{
if (len-- == 0)
return false;
/* next char has to be quote */
if (*cp != '"')
return false;
}
}
if (*cp != '"')
return false;
}
else
{
/* Doesn't allow national characters in sql name :( */
for (; len-- > 0; cp++)
if (!isalnum(*cp) && *cp != '_')
return false;
}
return true;
}
Datum
dbms_assert_simple_sql_name(PG_FUNCTION_ARGS)
{
text *sname;
int len;
char *cp;
if (PG_ARGISNULL(0))
ISNOT_SIMPLE_SQL_NAME_EXCEPTION();
sname = PG_GETARG_TEXT_P(0);
if (EMPTY_STR(sname))
ISNOT_SIMPLE_SQL_NAME_EXCEPTION();
len = VARSIZE(sname) - VARHDRSZ;
cp = VARDATA(sname);
if (!check_sql_name(cp, len))
ISNOT_SIMPLE_SQL_NAME_EXCEPTION();
PG_RETURN_TEXT_P(sname);
}
/****************************************************************
* DBMS_ASSERT.OBJECT_NAME
*
* Syntax:
* FUNCTION OBJECT_NAME(str varchar) RETURNS varchar;
*
* Purpouse:
* Verifies that input string is qualified SQL identifier of
* an existing SQL object.
* Exception: 44002 Invalid object name
*
****************************************************************/
Datum
dbms_assert_object_name(PG_FUNCTION_ARGS)
{
List *names;
text *str;
char *object_name;
Oid classId;
if (PG_ARGISNULL(0))
INVALID_OBJECT_NAME_EXCEPTION();
str = PG_GETARG_TEXT_P(0);
if (EMPTY_STR(str))
INVALID_OBJECT_NAME_EXCEPTION();
object_name = text_to_cstring(str);
#ifdef GP_VERSION_NUM
names = stringToQualifiedNameList(object_name, "dbms");
#else
names = stringToQualifiedNameList(object_name);
#endif
classId = RangeVarGetRelid(makeRangeVarFromNameList(names), true, true /*allowHcatalog*/);
if (!OidIsValid(classId))
INVALID_OBJECT_NAME_EXCEPTION();
PG_RETURN_TEXT_P(str);
}