| /* |
| * 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" |
| |
| /* |
| * 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)", |
| 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; |
| /* greater than MAXINT8LEN+1 */ |
| char buf[32]; |
| 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, -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, -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, -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, -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; |
| |
| 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); |
| |
| AlterTable(relid, AccessExclusiveLock, tbl_stmt); |
| |
| 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 */ |
| } |