/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
#include "postgres.h"
|
|
#include "access/heapam.h"
|
#include "access/xact.h"
|
#include "catalog/dependency.h"
|
#include "catalog/namespace.h"
|
#include "catalog/objectaddress.h"
|
#include "catalog/pg_class_d.h"
|
#include "commands/defrem.h"
|
#include "commands/sequence.h"
|
#include "commands/tablecmds.h"
|
#include "miscadmin.h"
|
#include "nodes/makefuncs.h"
|
#include "nodes/nodes.h"
|
#include "nodes/parsenodes.h"
|
#include "nodes/pg_list.h"
|
#include "nodes/plannodes.h"
|
#include "nodes/primnodes.h"
|
#include "nodes/value.h"
|
#include "parser/parse_node.h"
|
#include "parser/parser.h"
|
#include "storage/lockdefs.h"
|
#include "tcop/dest.h"
|
#include "tcop/utility.h"
|
#include "utils/acl.h"
|
#include "utils/builtins.h"
|
#include "utils/inval.h"
|
#include "utils/lsyscache.h"
|
|
#include "catalog/ag_graph.h"
|
#include "catalog/ag_label.h"
|
#include "commands/label_commands.h"
|
#include "utils/ag_cache.h"
|
#include "utils/agtype.h"
|
#include "utils/graphid.h"
|
#include "utils/name_validation.h"
|
|
#include "pg_fix.h"
|
|
/*
|
* Relation name doesn't have to be label name but the same name is used so
|
* that users can find the backed relation for a label only by its name.
|
*/
|
#define gen_label_relation_name(label_name) (label_name)
|
|
static void create_table_for_label(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name, char label_type,
|
List *parents);
|
|
// common
|
static List *create_edge_table_elements(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name);
|
static List *create_vertex_table_elements(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name);
|
static void create_sequence_for_label(RangeVar *seq_range_var);
|
static Constraint *build_pk_constraint(void);
|
static Constraint *build_id_default(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name);
|
static FuncCall *build_id_default_func_expr(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name);
|
static Constraint *build_not_null_constraint(void);
|
static Constraint *build_properties_default(void);
|
static void alter_sequence_owned_by_for_label(RangeVar *seq_range_var,
|
char *rel_name);
|
static int32 get_new_label_id(Oid graph_oid, Oid nsp_id);
|
static void change_label_id_default(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name,
|
Oid relid);
|
|
// drop
|
static void remove_relation(List *qname);
|
static void range_var_callback_for_remove_relation(const RangeVar *rel,
|
Oid rel_oid,
|
Oid odl_rel_oid,
|
void *arg);
|
|
|
|
PG_FUNCTION_INFO_V1(create_vlabel);
|
|
/*
|
* This is a callback function
|
* This function will be called when the user will call SELECT create_vlabel.
|
* The function takes two parameters
|
* 1. Graph name
|
* 2. Label Name
|
* Function will create a vertex label
|
* Function returns an error if graph or label names or not provided
|
*/
|
|
Datum create_vlabel(PG_FUNCTION_ARGS)
|
{
|
char *graph;
|
Name graph_name;
|
char *graph_name_str;
|
Oid graph_oid;
|
List *parent;
|
|
RangeVar *rv;
|
|
char *label;
|
Name label_name;
|
char *label_name_str;
|
|
// checking if user has not provided the graph name
|
if (PG_ARGISNULL(0))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("graph name must not be NULL")));
|
}
|
|
// checking if user has not provided the label name
|
if (PG_ARGISNULL(1))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("label name must not be NULL")));
|
}
|
|
graph_name = PG_GETARG_NAME(0);
|
label_name = PG_GETARG_NAME(1);
|
|
graph_name_str = NameStr(*graph_name);
|
label_name_str = NameStr(*label_name);
|
|
// Check if graph does not exist
|
if (!graph_exists(graph_name_str))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("graph \"%s\" does not exist.", graph_name_str)));
|
}
|
|
graph_oid = get_graph_oid(graph_name_str);
|
|
// Check if label with the input name already exists
|
if (label_exists(label_name_str, graph_oid))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("label \"%s\" already exists", label_name_str)));
|
}
|
|
//Create the default label tables
|
graph = graph_name->data;
|
label = label_name->data;
|
|
rv = get_label_range_var(graph, graph_oid, AG_DEFAULT_LABEL_VERTEX);
|
|
parent = list_make1(rv);
|
|
create_label(graph, label, LABEL_TYPE_VERTEX, parent);
|
|
ereport(NOTICE,
|
(errmsg("VLabel \"%s\" has been created", NameStr(*label_name))));
|
|
PG_RETURN_VOID();
|
}
|
|
PG_FUNCTION_INFO_V1(create_elabel);
|
|
/*
|
* This is a callback function
|
* This function will be called when the user will call SELECT create_elabel.
|
* The function takes two parameters
|
* 1. Graph name
|
* 2. Label Name
|
* Function will create an edge label
|
* Function returns an error if graph or label names or not provided
|
*/
|
|
Datum create_elabel(PG_FUNCTION_ARGS)
|
{
|
char *graph;
|
Name graph_name;
|
char *graph_name_str;
|
Oid graph_oid;
|
List *parent;
|
|
RangeVar *rv;
|
|
char *label;
|
Name label_name;
|
char *label_name_str;
|
|
// checking if user has not provided the graph name
|
if (PG_ARGISNULL(0))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("graph name must not be NULL")));
|
}
|
|
// checking if user has not provided the label name
|
if (PG_ARGISNULL(1))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("label name must not be NULL")));
|
}
|
|
graph_name = PG_GETARG_NAME(0);
|
label_name = PG_GETARG_NAME(1);
|
|
graph_name_str = NameStr(*graph_name);
|
label_name_str = NameStr(*label_name);
|
|
// Check if graph does not exist
|
if (!graph_exists(graph_name_str))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("graph \"%s\" does not exist.", graph_name_str)));
|
}
|
|
graph_oid = get_graph_oid(graph_name_str);
|
|
// Check if label with the input name already exists
|
if (label_exists(label_name_str, graph_oid))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("label \"%s\" already exists", label_name_str)));
|
}
|
|
//Create the default label tables
|
graph = graph_name->data;
|
label = label_name->data;
|
|
rv = get_label_range_var(graph, graph_oid, AG_DEFAULT_LABEL_EDGE);
|
|
parent = list_make1(rv);
|
create_label(graph, label, LABEL_TYPE_EDGE, parent);
|
|
ereport(NOTICE,
|
(errmsg("ELabel \"%s\" has been created", NameStr(*label_name))));
|
|
PG_RETURN_VOID();
|
}
|
|
/*
|
* For the new label, create an entry in ag_catalog.ag_label, create a
|
* new table and sequence. Returns the oid from the new tuple in
|
* ag_catalog.ag_label.
|
*/
|
void create_label(char *graph_name, char *label_name, char label_type,
|
List *parents)
|
{
|
graph_cache_data *cache_data;
|
Oid graph_oid;
|
Oid nsp_id;
|
char *schema_name;
|
char *rel_name;
|
char *seq_name;
|
RangeVar *seq_range_var;
|
int32 label_id;
|
Oid relation_id;
|
|
if (!is_valid_label(label_name, label_type))
|
{
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("label name is invalid")));
|
}
|
|
if (!is_valid_label(label_name, label_type))
|
{
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("label name is invalid")));
|
}
|
|
cache_data = search_graph_name_cache(graph_name);
|
if (!cache_data)
|
{
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("graph \"%s\" does not exist", graph_name)));
|
}
|
graph_oid = cache_data->oid;
|
nsp_id = cache_data->namespace;
|
|
// create a sequence for the new label to generate unique IDs for vertices
|
schema_name = get_namespace_name(nsp_id);
|
rel_name = gen_label_relation_name(label_name);
|
seq_name = ChooseRelationName(rel_name, "id", "seq", nsp_id, false);
|
seq_range_var = makeRangeVar(schema_name, seq_name, -1);
|
create_sequence_for_label(seq_range_var);
|
|
// create a table for the new label
|
create_table_for_label(graph_name, label_name, schema_name, rel_name,
|
seq_name, label_type, parents);
|
|
// record the new label in ag_label
|
relation_id = get_relname_relid(rel_name, nsp_id);
|
|
// If a label has parents, switch the parents id default, with its own.
|
if (list_length(parents) != 0)
|
change_label_id_default(graph_name, label_name, schema_name, seq_name,
|
relation_id);
|
|
// associate the sequence with the "id" column
|
alter_sequence_owned_by_for_label(seq_range_var, rel_name);
|
|
// get a new "id" for the new label
|
label_id = get_new_label_id(graph_oid, nsp_id);
|
|
insert_label(label_name, graph_oid, label_id, label_type,
|
relation_id, seq_name);
|
|
CommandCounterIncrement();
|
}
|
|
// CREATE TABLE `schema_name`.`rel_name` (
|
// "id" graphid PRIMARY KEY DEFAULT "ag_catalog"."_graphid"(...),
|
// "start_id" graphid NOT NULL note: only for edge labels
|
// "end_id" graphid NOT NULL note: only for edge labels
|
// "properties" agtype NOT NULL DEFAULT "ag_catalog"."agtype_build_map"()
|
// )
|
static void create_table_for_label(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name, char label_type,
|
List *parents)
|
{
|
CreateStmt *create_stmt;
|
PlannedStmt *wrapper;
|
|
create_stmt = makeNode(CreateStmt);
|
|
// relpersistence is set to RELPERSISTENCE_PERMANENT by makeRangeVar()
|
create_stmt->relation = makeRangeVar(schema_name, rel_name, -1);
|
|
/*
|
* When a new table has parents, do not create a column definition list.
|
* Use the parents' column definition list instead, via Postgres'
|
* inheritance system.
|
*/
|
if (list_length(parents) != 0)
|
create_stmt->tableElts = NIL;
|
else if (label_type == LABEL_TYPE_EDGE)
|
create_stmt->tableElts = create_edge_table_elements(
|
graph_name, label_name, schema_name, rel_name, seq_name);
|
else if (label_type == LABEL_TYPE_VERTEX)
|
create_stmt->tableElts = create_vertex_table_elements(
|
graph_name, label_name, schema_name, rel_name, seq_name);
|
else
|
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
|
errmsg("undefined label type \'%c\'", label_type)));
|
|
create_stmt->inhRelations = parents;
|
create_stmt->partbound = NULL;
|
create_stmt->ofTypename = NULL;
|
create_stmt->constraints = NIL;
|
create_stmt->options = NIL;
|
create_stmt->oncommit = ONCOMMIT_NOOP;
|
create_stmt->tablespacename = NULL;
|
create_stmt->if_not_exists = false;
|
|
wrapper = makeNode(PlannedStmt);
|
wrapper->commandType = CMD_UTILITY;
|
wrapper->canSetTag = false;
|
wrapper->utilityStmt = (Node *)create_stmt;
|
wrapper->stmt_location = -1;
|
wrapper->stmt_len = 0;
|
|
ProcessUtility(wrapper, "(generated CREATE TABLE command)", false,
|
PROCESS_UTILITY_SUBCOMMAND, NULL, NULL, None_Receiver,
|
NULL);
|
// CommandCounterIncrement() is called in ProcessUtility()
|
}
|
|
// CREATE TABLE `schema_name`.`rel_name` (
|
// "id" graphid PRIMARY KEY DEFAULT "ag_catalog"."_graphid"(...),
|
// "start_id" graphid NOT NULL
|
// "end_id" graphid NOT NULL
|
// "properties" agtype NOT NULL DEFAULT "ag_catalog"."agtype_build_map"()
|
// )
|
static List *create_edge_table_elements(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name)
|
{
|
ColumnDef *id;
|
ColumnDef *start_id;
|
ColumnDef *end_id;
|
ColumnDef *props;
|
|
// "id" graphid PRIMARY KEY DEFAULT "ag_catalog"."_graphid"(...)
|
id = makeColumnDef(AG_EDGE_COLNAME_ID, GRAPHIDOID, -1, InvalidOid);
|
id->constraints = list_make2(build_pk_constraint(),
|
build_id_default(graph_name, label_name,
|
schema_name, seq_name));
|
|
// "start_id" graphid NOT NULL
|
start_id = makeColumnDef(AG_EDGE_COLNAME_START_ID, GRAPHIDOID, -1,
|
InvalidOid);
|
start_id->constraints = list_make1(build_not_null_constraint());
|
|
// "end_id" graphid NOT NULL
|
end_id = makeColumnDef(AG_EDGE_COLNAME_END_ID, GRAPHIDOID, -1, InvalidOid);
|
end_id->constraints = list_make1(build_not_null_constraint());
|
|
// "properties" agtype NOT NULL DEFAULT "ag_catalog"."agtype_build_map"()
|
props = makeColumnDef(AG_EDGE_COLNAME_PROPERTIES, AGTYPEOID, -1,
|
InvalidOid);
|
props->constraints = list_make2(build_not_null_constraint(),
|
build_properties_default());
|
|
return list_make4(id, start_id, end_id, props);
|
}
|
|
// CREATE TABLE `schema_name`.`rel_name` (
|
// "id" graphid PRIMARY KEY DEFAULT "ag_catalog"."_graphid"(...),
|
// "properties" agtype NOT NULL DEFAULT "ag_catalog"."agtype_build_map"()
|
// )
|
static List *create_vertex_table_elements(char *graph_name, char *label_name,
|
char *schema_name, char *rel_name,
|
char *seq_name)
|
{
|
ColumnDef *id;
|
ColumnDef *props;
|
|
// "id" graphid PRIMARY KEY DEFAULT "ag_catalog"."_graphid"(...)
|
id = makeColumnDef(AG_VERTEX_COLNAME_ID, GRAPHIDOID, -1, InvalidOid);
|
id->constraints = list_make2(build_pk_constraint(),
|
build_id_default(graph_name, label_name,
|
schema_name, seq_name));
|
|
// "properties" agtype NOT NULL DEFAULT "ag_catalog"."agtype_build_map"()
|
props = makeColumnDef(AG_VERTEX_COLNAME_PROPERTIES, AGTYPEOID, -1,
|
InvalidOid);
|
props->constraints = list_make2(build_not_null_constraint(),
|
build_properties_default());
|
|
return list_make2(id, props);
|
}
|
|
// CREATE SEQUENCE `seq_range_var` MAXVALUE `LOCAL_ID_MAX`
|
static void create_sequence_for_label(RangeVar *seq_range_var)
|
{
|
ParseState *pstate;
|
CreateSeqStmt *seq_stmt;
|
char buf[32]; // greater than MAXINT8LEN+1
|
DefElem *maxvalue;
|
|
pstate = make_parsestate(NULL);
|
pstate->p_sourcetext = "(generated CREATE SEQUENCE command)";
|
|
seq_stmt = makeNode(CreateSeqStmt);
|
seq_stmt->sequence = seq_range_var;
|
pg_lltoa(ENTRY_ID_MAX, buf);
|
maxvalue = makeDefElem("maxvalue", (Node *)makeFloat(pstrdup(buf)), -1);
|
seq_stmt->options = list_make1(maxvalue);
|
seq_stmt->ownerId = InvalidOid;
|
seq_stmt->for_identity = false;
|
seq_stmt->if_not_exists = false;
|
|
DefineSequence(pstate, seq_stmt);
|
CommandCounterIncrement();
|
}
|
|
/*
|
* Builds the primary key constraint for when a table is created.
|
*/
|
static Constraint *build_pk_constraint(void)
|
{
|
Constraint *pk;
|
|
pk = makeNode(Constraint);
|
pk->contype = CONSTR_PRIMARY;
|
pk->location = -1;
|
pk->keys = NULL;
|
pk->options = NIL;
|
pk->indexname = NULL;
|
pk->indexspace = NULL;
|
|
return pk;
|
}
|
|
/*
|
* Construct a FuncCall node that will create the default logic for the label's
|
* id.
|
*/
|
static FuncCall *build_id_default_func_expr(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name)
|
{
|
List *label_id_func_name;
|
A_Const *graph_name_const;
|
A_Const *label_name_const;
|
List *label_id_func_args;
|
FuncCall *label_id_func;
|
List *nextval_func_name;
|
char *qualified_seq_name;
|
A_Const *qualified_seq_name_const;
|
TypeCast *regclass_cast;
|
List *nextval_func_args;
|
FuncCall *nextval_func;
|
List *graphid_func_name;
|
List *graphid_func_args;
|
FuncCall *graphid_func;
|
|
// Build a node that gets the label id
|
label_id_func_name = list_make2(makeString("ag_catalog"),
|
makeString("_label_id"));
|
graph_name_const = makeNode(A_Const);
|
graph_name_const->val.type = T_String;
|
graph_name_const->val.val.str = graph_name;
|
graph_name_const->location = -1;
|
label_name_const = makeNode(A_Const);
|
label_name_const->val.type = T_String;
|
label_name_const->val.val.str = label_name;
|
label_name_const->location = -1;
|
label_id_func_args = list_make2(graph_name_const, label_name_const);
|
label_id_func = makeFuncCall(label_id_func_name, label_id_func_args, COERCE_SQL_SYNTAX, -1);
|
|
//Build a node that will get the next val from the label's sequence
|
nextval_func_name = SystemFuncName("nextval");
|
qualified_seq_name = quote_qualified_identifier(schema_name, seq_name);
|
qualified_seq_name_const = makeNode(A_Const);
|
qualified_seq_name_const->val.type = T_String;
|
qualified_seq_name_const->val.val.str = qualified_seq_name;
|
qualified_seq_name_const->location = -1;
|
regclass_cast = makeNode(TypeCast);
|
regclass_cast->typeName = SystemTypeName("regclass");
|
regclass_cast->arg = (Node *)qualified_seq_name_const;
|
regclass_cast->location = -1;
|
nextval_func_args = list_make1(regclass_cast);
|
nextval_func = makeFuncCall(nextval_func_name, nextval_func_args, COERCE_SQL_SYNTAX, -1);
|
|
/*
|
* Build a node that constructs the graphid from the label id function
|
* and the next val function for the given sequence.
|
*/
|
graphid_func_name = list_make2(makeString("ag_catalog"),
|
makeString("_graphid"));
|
graphid_func_args = list_make2(label_id_func, nextval_func);
|
graphid_func = makeFuncCall(graphid_func_name, graphid_func_args, COERCE_SQL_SYNTAX, -1);
|
|
return graphid_func;
|
}
|
|
/*
|
* Construct a default constraint on the id column for a newly created table
|
*/
|
static Constraint *build_id_default(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name)
|
{
|
FuncCall *graphid_func;
|
Constraint *id_default;
|
|
graphid_func = build_id_default_func_expr(graph_name, label_name,
|
schema_name, seq_name);
|
|
id_default = makeNode(Constraint);
|
id_default->contype = CONSTR_DEFAULT;
|
id_default->location = -1;
|
id_default->raw_expr = (Node *)graphid_func;
|
id_default->cooked_expr = NULL;
|
|
return id_default;
|
}
|
|
// NOT NULL
|
static Constraint *build_not_null_constraint(void)
|
{
|
Constraint *not_null;
|
|
not_null = makeNode(Constraint);
|
not_null->contype = CONSTR_NOTNULL;
|
not_null->location = -1;
|
|
return not_null;
|
}
|
|
// DEFAULT "ag_catalog"."agtype_build_map"()
|
static Constraint *build_properties_default(void)
|
{
|
List *func_name;
|
FuncCall *func;
|
Constraint *props_default;
|
|
// "ag_catalog"."agtype_build_map"()
|
func_name = list_make2(makeString("ag_catalog"),
|
makeString("agtype_build_map"));
|
func = makeFuncCall(func_name, NIL, COERCE_SQL_SYNTAX, -1);
|
|
props_default = makeNode(Constraint);
|
props_default->contype = CONSTR_DEFAULT;
|
props_default->location = -1;
|
props_default->raw_expr = (Node *)func;
|
props_default->cooked_expr = NULL;
|
|
return props_default;
|
}
|
|
/*
|
* Alter the default constraint on the label's id to the use the given
|
* sequence.
|
*/
|
static void change_label_id_default(char *graph_name, char *label_name,
|
char *schema_name, char *seq_name,
|
Oid relid)
|
{
|
ParseState *pstate;
|
AlterTableStmt *tbl_stmt;
|
AlterTableCmd *tbl_cmd;
|
RangeVar *rv;
|
FuncCall *func_call;
|
AlterTableUtilityContext atuc;
|
|
func_call = build_id_default_func_expr(graph_name, label_name, schema_name,
|
seq_name);
|
|
rv = makeRangeVar(schema_name, label_name, -1);
|
|
pstate = make_parsestate(NULL);
|
pstate->p_sourcetext = "(generated ALTER TABLE command)";
|
|
tbl_stmt = makeNode(AlterTableStmt);
|
tbl_stmt->relation = rv;
|
tbl_stmt->missing_ok = false;
|
|
tbl_cmd = makeNode(AlterTableCmd);
|
tbl_cmd->subtype = AT_ColumnDefault;
|
tbl_cmd->name = "id";
|
tbl_cmd->def = (Node *)func_call;
|
|
tbl_stmt->cmds = list_make1(tbl_cmd);
|
|
atuc.relid = relid;
|
atuc.queryEnv = pstate->p_queryEnv;
|
atuc.queryString = pstate->p_sourcetext;
|
|
AlterTable(tbl_stmt, AccessExclusiveLock, &atuc);
|
|
CommandCounterIncrement();
|
}
|
|
// CREATE SEQUENCE `seq_range_var` OWNED BY `schema_name`.`rel_name`."id"
|
static void alter_sequence_owned_by_for_label(RangeVar *seq_range_var,
|
char *rel_name)
|
{
|
ParseState *pstate;
|
AlterSeqStmt *seq_stmt;
|
char *schema_name;
|
List *id;
|
DefElem *owned_by;
|
|
pstate = make_parsestate(NULL);
|
pstate->p_sourcetext = "(generated ALTER SEQUENCE command)";
|
|
seq_stmt = makeNode(AlterSeqStmt);
|
seq_stmt->sequence = seq_range_var;
|
schema_name = seq_range_var->schemaname;
|
id = list_make3(makeString(schema_name), makeString(rel_name),
|
makeString("id"));
|
owned_by = makeDefElem("owned_by", (Node *)id, -1);
|
seq_stmt->options = list_make1(owned_by);
|
seq_stmt->for_identity = false;
|
seq_stmt->missing_ok = false;
|
|
AlterSequence(pstate, seq_stmt);
|
CommandCounterIncrement();
|
}
|
|
static int32 get_new_label_id(Oid graph_oid, Oid nsp_id)
|
{
|
Oid seq_id;
|
int cnt;
|
|
// get the OID of the sequence
|
seq_id = get_relname_relid(LABEL_ID_SEQ_NAME, nsp_id);
|
if (!OidIsValid(seq_id))
|
{
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE),
|
errmsg("sequence \"%s\" does not exists",
|
LABEL_ID_SEQ_NAME)));
|
}
|
|
for (cnt = LABEL_ID_MIN; cnt <= LABEL_ID_MAX; cnt++)
|
{
|
int32 label_id;
|
|
// the data type of the sequence is integer (int4)
|
label_id = (int32) nextval_internal(seq_id, true);
|
Assert(label_id_is_valid(label_id));
|
if (!label_id_exists(graph_oid, label_id))
|
{
|
return (int32) label_id;
|
}
|
}
|
|
ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
errmsg("no more new labels are available"),
|
errhint("The maximum number of labels in a graph is %d",
|
LABEL_ID_MAX)));
|
return 0;
|
}
|
|
PG_FUNCTION_INFO_V1(drop_label);
|
|
Datum drop_label(PG_FUNCTION_ARGS)
|
{
|
Name graph_name;
|
Name label_name;
|
bool force;
|
char *graph_name_str;
|
graph_cache_data *cache_data;
|
Oid graph_oid;
|
Oid nsp_id;
|
char *label_name_str;
|
Oid label_relation;
|
char *schema_name;
|
char *rel_name;
|
List *qname;
|
|
if (PG_ARGISNULL(0))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("graph name must not be NULL")));
|
}
|
if (PG_ARGISNULL(1))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("label name must not be NULL")));
|
}
|
graph_name = PG_GETARG_NAME(0);
|
label_name = PG_GETARG_NAME(1);
|
force = PG_GETARG_BOOL(2);
|
|
graph_name_str = NameStr(*graph_name);
|
cache_data = search_graph_name_cache(graph_name_str);
|
if (!cache_data)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
errmsg("graph \"%s\" does not exist", graph_name_str)));
|
}
|
graph_oid = cache_data->oid;
|
nsp_id = cache_data->namespace;
|
|
label_name_str = NameStr(*label_name);
|
label_relation = get_label_relation(label_name_str, graph_oid);
|
if (!OidIsValid(label_relation))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
errmsg("label \"%s\" does not exist", label_name_str)));
|
}
|
|
if (force)
|
{
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("force option is not supported yet")));
|
}
|
|
/* validate schema_name */
|
schema_name = get_namespace_name(nsp_id);
|
if (schema_name == NULL)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
errmsg("schema_name not found for namespace id \"%d\"",
|
nsp_id)));
|
}
|
|
/* validate rel_name */
|
rel_name = get_rel_name(label_relation);
|
if (rel_name == NULL)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
errmsg("rel_name not found for label \"%s\"",
|
label_name_str)));
|
}
|
|
/* build qualified name */
|
qname = list_make2(makeString(schema_name), makeString(rel_name));
|
|
remove_relation(qname);
|
// CommandCounterIncrement() is called in performDeletion()
|
|
// delete_label() will be called in object_access()
|
|
ereport(NOTICE, (errmsg("label \"%s\".\"%s\" has been dropped",
|
graph_name_str, label_name_str)));
|
|
PG_RETURN_VOID();
|
}
|
|
// See RemoveRelations() for more details.
|
static void remove_relation(List *qname)
|
{
|
RangeVar *rel;
|
Oid rel_oid;
|
ObjectAddress address;
|
|
AssertArg(list_length(qname) == 2);
|
|
// concurrent is false so lockmode is AccessExclusiveLock
|
|
// relkind is RELKIND_RELATION
|
|
AcceptInvalidationMessages();
|
|
rel = makeRangeVarFromNameList(qname);
|
rel_oid = RangeVarGetRelidExtended(rel, AccessExclusiveLock,
|
RVR_MISSING_OK,
|
range_var_callback_for_remove_relation,
|
NULL);
|
|
if (!OidIsValid(rel_oid))
|
{
|
/*
|
* before calling this function, this condition is already checked in
|
* drop_graph()
|
*/
|
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
|
errmsg("ag_label catalog is corrupted"),
|
errhint("Table \"%s\".\"%s\" does not exist",
|
rel->schemaname, rel->relname)));
|
}
|
|
// concurrent is false
|
|
ObjectAddressSet(address, RelationRelationId, rel_oid);
|
|
/*
|
* set PERFORM_DELETION_INTERNAL flag so that object_access_hook can ignore
|
* this deletion
|
*/
|
performDeletion(&address, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
|
}
|
|
// See RangeVarCallbackForDropRelation() for more details.
|
static void range_var_callback_for_remove_relation(const RangeVar *rel,
|
Oid rel_oid,
|
Oid odl_rel_oid,
|
void *arg)
|
{
|
/*
|
* arg is NULL because relkind is always RELKIND_RELATION, heapOid is
|
* always InvalidOid, partParentOid is always InvalidOid, and concurrent is
|
* always false. See RemoveRelations() for more details.
|
*/
|
|
// heapOid is always InvalidOid
|
|
// partParentOid is always InvalidOid
|
|
if (!OidIsValid(rel_oid))
|
return;
|
|
// classform->relkind is always RELKIND_RELATION
|
|
// relkind == expected_relkind
|
|
if (!pg_class_ownercheck(rel_oid, GetUserId()) &&
|
!pg_namespace_ownercheck(get_rel_namespace(rel_oid), GetUserId()))
|
{
|
aclcheck_error(ACLCHECK_NOT_OWNER,
|
get_relkind_objtype(get_rel_relkind(rel_oid)),
|
rel->relname);
|
}
|
|
// the target relation is not system class
|
|
// relkind is always RELKIND_RELATION
|
|
// is_partition is false
|
}
|