From b8d226b4f9691c7afb986dbaaf3f6ff7b203d1b5 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Wed, 18 Mar 2015 14:48:02 -0300 Subject: [PATCH] Setup cursor position for schema-qualified elements This makes any errors thrown while looking up such schemas report the position of the error. Author: Ryan Kelly Reviewed by: Jeevan Chalke, Tom Lane --- src/backend/parser/parse_func.c | 7 +++++++ src/backend/parser/parse_oper.c | 24 ++++++++++++++++------ src/backend/parser/parse_type.c | 5 +++++ src/backend/parser/parse_utilcmd.c | 13 ++++++++---- src/test/regress/expected/create_table.out | 4 ++++ 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 53bbaecac3..1385776f6b 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -93,6 +93,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Oid vatype; FuncDetailCode fdresult; char aggkind = 0; + ParseCallbackState pcbstate; /* * If there's an aggregate filter, transform it using transformWhereClause @@ -235,12 +236,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * type. We'll fix up the variadic case below. We may also have to deal * with default arguments. */ + + setup_parser_errposition_callback(&pcbstate, pstate, location); + fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, &funcid, &rettype, &retset, &nvargs, &vatype, &declared_arg_types, &argdefaults); + + cancel_parser_errposition_callback(&pcbstate); + if (fdresult == FUNCDETAIL_COERCION) { /* diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 10de97b772..553260c835 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -75,8 +75,9 @@ static const char *op_signature_string(List *op, char oprkind, static void op_error(ParseState *pstate, List *op, char oprkind, Oid arg1, Oid arg2, FuncDetailCode fdresult, int location); -static bool make_oper_cache_key(OprCacheKey *key, List *opname, - Oid ltypeId, Oid rtypeId); +static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key, + List *opname, Oid ltypeId, Oid rtypeId, + int location); static Oid find_oper_cache_entry(OprCacheKey *key); static void make_oper_cache_entry(OprCacheKey *key, Oid opr_oid); static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue); @@ -383,7 +384,8 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, /* * Try to find the mapping in the lookaside cache. */ - key_ok = make_oper_cache_key(&key, opname, ltypeId, rtypeId); + key_ok = make_oper_cache_key(pstate, &key, opname, ltypeId, rtypeId, location); + if (key_ok) { operOid = find_oper_cache_entry(&key); @@ -529,7 +531,8 @@ right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) /* * Try to find the mapping in the lookaside cache. */ - key_ok = make_oper_cache_key(&key, op, arg, InvalidOid); + key_ok = make_oper_cache_key(pstate, &key, op, arg, InvalidOid, location); + if (key_ok) { operOid = find_oper_cache_entry(&key); @@ -607,7 +610,8 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) /* * Try to find the mapping in the lookaside cache. */ - key_ok = make_oper_cache_key(&key, op, InvalidOid, arg); + key_ok = make_oper_cache_key(pstate, &key, op, InvalidOid, arg, location); + if (key_ok) { operOid = find_oper_cache_entry(&key); @@ -1006,9 +1010,13 @@ static HTAB *OprCacheHash = NULL; * * Returns TRUE if successful, FALSE if the search_path overflowed * (hence no caching is possible). + * + * pstate/location are used only to report the error position; pass NULL/-1 + * if not available. */ static bool -make_oper_cache_key(OprCacheKey *key, List *opname, Oid ltypeId, Oid rtypeId) +make_oper_cache_key(ParseState *pstate, OprCacheKey *key, List *opname, + Oid ltypeId, Oid rtypeId, int location) { char *schemaname; char *opername; @@ -1026,8 +1034,12 @@ make_oper_cache_key(OprCacheKey *key, List *opname, Oid ltypeId, Oid rtypeId) if (schemaname) { + ParseCallbackState pcbstate; + /* search only in exact schema given */ + setup_parser_errposition_callback(&pcbstate, pstate, location); key->search_path[0] = LookupExplicitNamespace(schemaname, false); + cancel_parser_errposition_callback(&pcbstate); } else { diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index ca5fbed34f..1ba6ca76f4 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -156,6 +156,9 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, { /* Look in specific schema only */ Oid namespaceId; + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, typeName->location); namespaceId = LookupExplicitNamespace(schemaname, missing_ok); if (OidIsValid(namespaceId)) @@ -164,6 +167,8 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, ObjectIdGetDatum(namespaceId)); else typoid = InvalidOid; + + cancel_parser_errposition_callback(&pcbstate); } else { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 1e6da9cc40..1bbed9582c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -149,6 +149,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) ListCell *elements; Oid namespaceid; Oid existing_relid; + ParseCallbackState pcbstate; /* * We must not scribble on the passed-in CreateStmt, so copy it. (This is @@ -156,15 +157,22 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) */ stmt = (CreateStmt *) copyObject(stmt); + /* Set up pstate */ + pstate = make_parsestate(NULL); + pstate->p_sourcetext = queryString; + /* * Look up the creation namespace. This also checks permissions on the * target namespace, locks it against concurrent drops, checks for a * preexisting relation in that namespace with the same name, and updates * stmt->relation->relpersistence if the selected namespace is temporary. */ + setup_parser_errposition_callback(&pcbstate, pstate, + stmt->relation->location); namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, &existing_relid); + cancel_parser_errposition_callback(&pcbstate); /* * If the relation already exists and the user specified "IF NOT EXISTS", @@ -190,10 +198,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) && stmt->relation->relpersistence != RELPERSISTENCE_TEMP) stmt->relation->schemaname = get_namespace_name(namespaceid); - /* Set up pstate and CreateStmtContext */ - pstate = make_parsestate(NULL); - pstate->p_sourcetext = queryString; - + /* Set up CreateStmtContext */ cxt.pstate = pstate; if (IsA(stmt, CreateForeignTableStmt)) { diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 35451d5af2..34b5fc113d 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -212,11 +212,15 @@ INSERT INTO unlogged1 VALUES (42); CREATE UNLOGGED TABLE public.unlogged2 (a int primary key); -- also OK CREATE UNLOGGED TABLE pg_temp.unlogged3 (a int primary key); -- not OK ERROR: only temporary relations may be created in temporary schemas +LINE 1: CREATE UNLOGGED TABLE pg_temp.unlogged3 (a int primary key); + ^ CREATE TABLE pg_temp.implicitly_temp (a int primary key); -- OK CREATE TEMP TABLE explicitly_temp (a int primary key); -- also OK CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key); -- also OK CREATE TEMP TABLE public.temp_to_perm (a int primary key); -- not OK ERROR: cannot create temporary relation in non-temporary schema +LINE 1: CREATE TEMP TABLE public.temp_to_perm (a int primary key); + ^ DROP TABLE unlogged1, public.unlogged2; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; CREATE TABLE as_select1 AS SELECT * FROM pg_class WHERE relkind = 'r'; -- 2.40.0