mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 00:03:00 -04:00
Compare commits
3 Commits
f374fb4aab
...
de2d393a8a
Author | SHA1 | Date | |
---|---|---|---|
|
de2d393a8a | ||
|
a0c19de115 | ||
|
1cf2dba84b |
@ -107,6 +107,7 @@ do { \
|
||||
static IndexScanDesc index_beginscan_internal(Relation indexRelation,
|
||||
int nkeys, int norderbys, Snapshot snapshot,
|
||||
ParallelIndexScanDesc pscan, bool temp_snap);
|
||||
static inline void validate_relation_kind(Relation r);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@ -135,12 +136,30 @@ index_open(Oid relationId, LOCKMODE lockmode)
|
||||
|
||||
r = relation_open(relationId, lockmode);
|
||||
|
||||
if (r->rd_rel->relkind != RELKIND_INDEX &&
|
||||
r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is not an index",
|
||||
RelationGetRelationName(r))));
|
||||
validate_relation_kind(r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* try_index_open - open a index relation by relation OID
|
||||
*
|
||||
* Same as index_open, except return NULL instead of failing
|
||||
* if the relation does not exist.
|
||||
* ----------------
|
||||
*/
|
||||
Relation
|
||||
try_index_open(Oid relationId, LOCKMODE lockmode)
|
||||
{
|
||||
Relation r;
|
||||
|
||||
r = try_relation_open(relationId, lockmode);
|
||||
|
||||
/* leave if index does not exist */
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
validate_relation_kind(r);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -168,6 +187,24 @@ index_close(Relation relation, LOCKMODE lockmode)
|
||||
UnlockRelationId(&relid, lockmode);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* validate_relation_kind - check the relation's kind
|
||||
*
|
||||
* Make sure relkind is an index or a partitioned index.
|
||||
* ----------------
|
||||
*/
|
||||
static inline void
|
||||
validate_relation_kind(Relation r)
|
||||
{
|
||||
if (r->rd_rel->relkind != RELKIND_INDEX &&
|
||||
r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is not an index",
|
||||
RelationGetRelationName(r))));
|
||||
}
|
||||
|
||||
|
||||
/* ----------------
|
||||
* index_insert - insert an index tuple into a relation
|
||||
* ----------------
|
||||
|
@ -3621,7 +3621,24 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
|
||||
* Open the target index relation and get an exclusive lock on it, to
|
||||
* ensure that no one else is touching this particular index.
|
||||
*/
|
||||
iRel = index_open(indexId, AccessExclusiveLock);
|
||||
if ((params->options & REINDEXOPT_MISSING_OK) != 0)
|
||||
iRel = try_index_open(indexId, AccessExclusiveLock);
|
||||
else
|
||||
iRel = index_open(indexId, AccessExclusiveLock);
|
||||
|
||||
/* if index relation is gone, leave */
|
||||
if (!iRel)
|
||||
{
|
||||
/* Roll back any GUC changes */
|
||||
AtEOXact_GUC(false, save_nestlevel);
|
||||
|
||||
/* Restore userid and security context */
|
||||
SetUserIdAndSecContext(save_userid, save_sec_context);
|
||||
|
||||
/* Close parent heap relation, but keep locks */
|
||||
table_close(heapRelation, NoLock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
|
||||
|
@ -138,6 +138,7 @@ typedef struct IndexOrderByDistance
|
||||
#define IndexScanIsValid(scan) PointerIsValid(scan)
|
||||
|
||||
extern Relation index_open(Oid relationId, LOCKMODE lockmode);
|
||||
extern Relation try_index_open(Oid relationId, LOCKMODE lockmode);
|
||||
extern void index_close(Relation relation, LOCKMODE lockmode);
|
||||
|
||||
extern bool index_insert(Relation indexRelation,
|
||||
|
@ -32,8 +32,9 @@ DATA = plpgsql.control plpgsql--1.0.sql
|
||||
|
||||
REGRESS_OPTS = --dbname=$(PL_TESTDB)
|
||||
|
||||
REGRESS = plpgsql_array plpgsql_call plpgsql_control plpgsql_copy plpgsql_domain \
|
||||
plpgsql_record plpgsql_cache plpgsql_simple plpgsql_transaction \
|
||||
REGRESS = plpgsql_array plpgsql_cache plpgsql_call plpgsql_control \
|
||||
plpgsql_copy plpgsql_domain plpgsql_misc \
|
||||
plpgsql_record plpgsql_simple plpgsql_transaction \
|
||||
plpgsql_trap plpgsql_trigger plpgsql_varprops
|
||||
|
||||
# where to find gen_keywordlist.pl and subsidiary files
|
||||
|
31
src/pl/plpgsql/src/expected/plpgsql_misc.out
Normal file
31
src/pl/plpgsql/src/expected/plpgsql_misc.out
Normal file
@ -0,0 +1,31 @@
|
||||
--
|
||||
-- Miscellaneous topics
|
||||
--
|
||||
-- Verify that we can parse new-style CREATE FUNCTION/PROCEDURE
|
||||
do
|
||||
$$
|
||||
declare procedure int; -- check we still recognize non-keywords as vars
|
||||
begin
|
||||
create function test1() returns int
|
||||
begin atomic
|
||||
select 2 + 2;
|
||||
end;
|
||||
create or replace procedure test2(x int)
|
||||
begin atomic
|
||||
select x + 2;
|
||||
end;
|
||||
end
|
||||
$$;
|
||||
\sf test1
|
||||
CREATE OR REPLACE FUNCTION public.test1()
|
||||
RETURNS integer
|
||||
LANGUAGE sql
|
||||
BEGIN ATOMIC
|
||||
SELECT (2 + 2);
|
||||
END
|
||||
\sf test2
|
||||
CREATE OR REPLACE PROCEDURE public.test2(IN x integer)
|
||||
LANGUAGE sql
|
||||
BEGIN ATOMIC
|
||||
SELECT (x + 2);
|
||||
END
|
@ -79,7 +79,8 @@ static PLpgSQL_expr *read_sql_expression2(int until, int until2,
|
||||
int *endtoken);
|
||||
static PLpgSQL_expr *read_sql_stmt(void);
|
||||
static PLpgSQL_type *read_datatype(int tok);
|
||||
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location);
|
||||
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location,
|
||||
PLword *word);
|
||||
static PLpgSQL_stmt_fetch *read_fetch_direction(void);
|
||||
static void complete_direction(PLpgSQL_stmt_fetch *fetch,
|
||||
bool *check_FROM);
|
||||
@ -1996,15 +1997,15 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
|
||||
*/
|
||||
stmt_execsql : K_IMPORT
|
||||
{
|
||||
$$ = make_execsql_stmt(K_IMPORT, @1);
|
||||
$$ = make_execsql_stmt(K_IMPORT, @1, NULL);
|
||||
}
|
||||
| K_INSERT
|
||||
{
|
||||
$$ = make_execsql_stmt(K_INSERT, @1);
|
||||
$$ = make_execsql_stmt(K_INSERT, @1, NULL);
|
||||
}
|
||||
| K_MERGE
|
||||
{
|
||||
$$ = make_execsql_stmt(K_MERGE, @1);
|
||||
$$ = make_execsql_stmt(K_MERGE, @1, NULL);
|
||||
}
|
||||
| T_WORD
|
||||
{
|
||||
@ -2015,7 +2016,7 @@ stmt_execsql : K_IMPORT
|
||||
if (tok == '=' || tok == COLON_EQUALS ||
|
||||
tok == '[' || tok == '.')
|
||||
word_is_not_variable(&($1), @1);
|
||||
$$ = make_execsql_stmt(T_WORD, @1);
|
||||
$$ = make_execsql_stmt(T_WORD, @1, &($1));
|
||||
}
|
||||
| T_CWORD
|
||||
{
|
||||
@ -2026,7 +2027,7 @@ stmt_execsql : K_IMPORT
|
||||
if (tok == '=' || tok == COLON_EQUALS ||
|
||||
tok == '[' || tok == '.')
|
||||
cword_is_not_variable(&($1), @1);
|
||||
$$ = make_execsql_stmt(T_CWORD, @1);
|
||||
$$ = make_execsql_stmt(T_CWORD, @1, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
@ -2943,8 +2944,13 @@ read_datatype(int tok)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a generic SQL statement. We have already read its first token;
|
||||
* firsttoken is that token's code and location its starting location.
|
||||
* If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
|
||||
*/
|
||||
static PLpgSQL_stmt *
|
||||
make_execsql_stmt(int firsttoken, int location)
|
||||
make_execsql_stmt(int firsttoken, int location, PLword *word)
|
||||
{
|
||||
StringInfoData ds;
|
||||
IdentifierLookup save_IdentifierLookup;
|
||||
@ -2957,9 +2963,16 @@ make_execsql_stmt(int firsttoken, int location)
|
||||
bool have_strict = false;
|
||||
int into_start_loc = -1;
|
||||
int into_end_loc = -1;
|
||||
int paren_depth = 0;
|
||||
int begin_depth = 0;
|
||||
bool in_routine_definition = false;
|
||||
int token_count = 0;
|
||||
char tokens[4]; /* records the first few tokens */
|
||||
|
||||
initStringInfo(&ds);
|
||||
|
||||
memset(tokens, 0, sizeof(tokens));
|
||||
|
||||
/* special lookup mode for identifiers within the SQL text */
|
||||
save_IdentifierLookup = plpgsql_IdentifierLookup;
|
||||
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
|
||||
@ -2968,6 +2981,12 @@ make_execsql_stmt(int firsttoken, int location)
|
||||
* Scan to the end of the SQL command. Identify any INTO-variables
|
||||
* clause lurking within it, and parse that via read_into_target().
|
||||
*
|
||||
* The end of the statement is defined by a semicolon ... except that
|
||||
* semicolons within parentheses or BEGIN/END blocks don't terminate a
|
||||
* statement. We follow psql's lead in not recognizing BEGIN/END except
|
||||
* after CREATE [OR REPLACE] {FUNCTION|PROCEDURE}. END can also appear
|
||||
* within a CASE construct, so we treat CASE/END like BEGIN/END.
|
||||
*
|
||||
* Because INTO is sometimes used in the main SQL grammar, we have to be
|
||||
* careful not to take any such usage of INTO as a PL/pgSQL INTO clause.
|
||||
* There are currently three such cases:
|
||||
@ -2993,13 +3012,50 @@ make_execsql_stmt(int firsttoken, int location)
|
||||
* break this logic again ... beware!
|
||||
*/
|
||||
tok = firsttoken;
|
||||
if (tok == T_WORD && strcmp(word->ident, "create") == 0)
|
||||
tokens[token_count] = 'c';
|
||||
token_count++;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
prev_tok = tok;
|
||||
tok = yylex();
|
||||
if (have_into && into_end_loc < 0)
|
||||
into_end_loc = yylloc; /* token after the INTO part */
|
||||
if (tok == ';')
|
||||
/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
|
||||
if (tokens[0] == 'c' && token_count < sizeof(tokens))
|
||||
{
|
||||
if (tok == K_OR)
|
||||
tokens[token_count] = 'o';
|
||||
else if (tok == T_WORD &&
|
||||
strcmp(yylval.word.ident, "replace") == 0)
|
||||
tokens[token_count] = 'r';
|
||||
else if (tok == T_WORD &&
|
||||
strcmp(yylval.word.ident, "function") == 0)
|
||||
tokens[token_count] = 'f';
|
||||
else if (tok == T_WORD &&
|
||||
strcmp(yylval.word.ident, "procedure") == 0)
|
||||
tokens[token_count] = 'f'; /* treat same as "function" */
|
||||
if (tokens[1] == 'f' ||
|
||||
(tokens[1] == 'o' && tokens[2] == 'r' && tokens[3] == 'f'))
|
||||
in_routine_definition = true;
|
||||
token_count++;
|
||||
}
|
||||
/* Track paren nesting (needed for CREATE RULE syntax) */
|
||||
if (tok == '(')
|
||||
paren_depth++;
|
||||
else if (tok == ')' && paren_depth > 0)
|
||||
paren_depth--;
|
||||
/* We need track BEGIN/END nesting only in a routine definition */
|
||||
if (in_routine_definition && paren_depth == 0)
|
||||
{
|
||||
if (tok == K_BEGIN || tok == K_CASE)
|
||||
begin_depth++;
|
||||
else if (tok == K_END && begin_depth > 0)
|
||||
begin_depth--;
|
||||
}
|
||||
/* Command-ending semicolon? */
|
||||
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
|
||||
break;
|
||||
if (tok == 0)
|
||||
yyerror("unexpected end of function definition");
|
||||
|
22
src/pl/plpgsql/src/sql/plpgsql_misc.sql
Normal file
22
src/pl/plpgsql/src/sql/plpgsql_misc.sql
Normal file
@ -0,0 +1,22 @@
|
||||
--
|
||||
-- Miscellaneous topics
|
||||
--
|
||||
|
||||
-- Verify that we can parse new-style CREATE FUNCTION/PROCEDURE
|
||||
do
|
||||
$$
|
||||
declare procedure int; -- check we still recognize non-keywords as vars
|
||||
begin
|
||||
create function test1() returns int
|
||||
begin atomic
|
||||
select 2 + 2;
|
||||
end;
|
||||
create or replace procedure test2(x int)
|
||||
begin atomic
|
||||
select x + 2;
|
||||
end;
|
||||
end
|
||||
$$;
|
||||
|
||||
\sf test1
|
||||
\sf test2
|
Loading…
x
Reference in New Issue
Block a user