This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new ad0a491 Issue 317: Graph naming convention (#349)
ad0a491 is described below
commit ad0a491d99b080ff34a607dba572c11fa832b679
Author: Rafsun Masud <[email protected]>
AuthorDate: Thu Dec 29 14:43:17 2022 -0500
Issue 317: Graph naming convention (#349)
* add name validation for graphs and labels
* update test script to use valid graph name
---
Makefile | 8 +-
regress/expected/catalog.out | 198 ++++++++---------
regress/expected/name_validation.out | 401 ++++++++++++++++++++++++++++++++++
regress/sql/catalog.sql | 62 +++---
regress/sql/name_validation.sql | 153 +++++++++++++
src/backend/commands/graph_commands.c | 16 +-
src/backend/commands/label_commands.c | 9 +-
src/backend/utils/name_validation.c | 91 ++++++++
src/include/utils/name_validation.h | 44 ++++
9 files changed, 843 insertions(+), 139 deletions(-)
diff --git a/Makefile b/Makefile
index e6024d6..b1d340c 100644
--- a/Makefile
+++ b/Makefile
@@ -68,7 +68,8 @@ OBJS = src/backend/age.o \
src/backend/utils/load/ag_load_labels.o \
src/backend/utils/load/ag_load_edges.o \
src/backend/utils/load/age_load.o \
- src/backend/utils/load/libcsv.o
+ src/backend/utils/load/libcsv.o \
+ src/backend/utils/name_validation.o
EXTENSION = age
@@ -90,13 +91,14 @@ REGRESS = scan \
cypher_with \
cypher_vle \
cypher_union \
- cypher_call \
+ cypher_call \
cypher_merge \
- age_global_graph \
+ age_global_graph \
age_load \
index \
analyze \
graph_generation \
+ name_validation \
drop
diff --git a/regress/expected/catalog.out b/regress/expected/catalog.out
index 4fca3e6..240ff17 100644
--- a/regress/expected/catalog.out
+++ b/regress/expected/catalog.out
@@ -21,75 +21,75 @@ SET search_path TO ag_catalog;
--
-- create_graph(), drop_label(), and drop_graph() tests
--
-SELECT create_graph('g');
-NOTICE: graph "g" has been created
+SELECT create_graph('graph');
+NOTICE: graph "graph" has been created
create_graph
--------------
(1 row)
-SELECT * FROM ag_graph WHERE name = 'g';
- name | namespace
-------+-----------
- g | g
+SELECT * FROM ag_graph WHERE name = 'graph';
+ name | namespace
+-------+-----------
+ graph | graph
(1 row)
-- create a label to test drop_label()
-SELECT * FROM cypher('g', $$CREATE (:l)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:l)$$) AS r(a agtype);
a
---
(0 rows)
-- test drop_label()
-SELECT drop_label('g', 'l');
-NOTICE: label "g"."l" has been dropped
+SELECT drop_label('graph', 'l');
+NOTICE: label "graph"."l" has been dropped
drop_label
------------
(1 row)
-- create a label to test drop_graph()
-SELECT * FROM cypher('g', $$CREATE (:v)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v)$$) AS r(a agtype);
a
---
(0 rows)
-- DROP SCHEMA ... CASCADE should fail
-DROP SCHEMA g CASCADE;
+DROP SCHEMA graph CASCADE;
NOTICE: drop cascades to 4 other objects
-DETAIL: drop cascades to sequence g._label_id_seq
-drop cascades to table g._ag_label_vertex
-drop cascades to table g._ag_label_edge
-drop cascades to table g.v
+DETAIL: drop cascades to sequence graph._label_id_seq
+drop cascades to table graph._ag_label_vertex
+drop cascades to table graph._ag_label_edge
+drop cascades to table graph.v
ERROR: table "v" is for label "v"
-- DROP TABLE ... should fail
-DROP TABLE g.v;
+DROP TABLE graph.v;
ERROR: table "v" is for label "v"
-- should fail (cascade = false)
-SELECT drop_graph('g');
-ERROR: cannot drop schema g because other objects depend on it
-DETAIL: table g._ag_label_vertex depends on schema g
-table g._ag_label_edge depends on schema g
-table g.v depends on schema g
+SELECT drop_graph('graph');
+ERROR: cannot drop schema graph because other objects depend on it
+DETAIL: table graph._ag_label_vertex depends on schema graph
+table graph._ag_label_edge depends on schema graph
+table graph.v depends on schema graph
HINT: Use DROP ... CASCADE to drop the dependent objects too.
-SELECT drop_graph('g', true);
+SELECT drop_graph('graph', true);
NOTICE: drop cascades to 3 other objects
-DETAIL: drop cascades to table g._ag_label_vertex
-drop cascades to table g._ag_label_edge
-drop cascades to table g.v
-NOTICE: graph "g" has been dropped
+DETAIL: drop cascades to table graph._ag_label_vertex
+drop cascades to table graph._ag_label_edge
+drop cascades to table graph.v
+NOTICE: graph "graph" has been dropped
drop_graph
------------
(1 row)
-SELECT count(*) FROM ag_graph WHERE name = 'g';
+SELECT count(*) FROM ag_graph WHERE name = 'graph';
count
-------
0
(1 row)
-SELECT count(*) FROM pg_namespace WHERE nspname = 'g';
+SELECT count(*) FROM pg_namespace WHERE nspname = 'graph';
count
-------
0
@@ -101,7 +101,7 @@ ERROR: graph name can not be NULL
SELECT drop_graph(NULL);
ERROR: graph name can not be NULL
SELECT create_graph('');
-ERROR: graph name can not be empty
+ERROR: graph name is invalid
--
-- alter_graph() RENAME function tests
--
@@ -216,119 +216,119 @@ HINT: valid operations: RENAME
--
-- label id test
--
-SELECT create_graph('g');
-NOTICE: graph "g" has been created
+SELECT create_graph('graph');
+NOTICE: graph "graph" has been created
create_graph
--------------
(1 row)
-- label id starts from 1
-SELECT * FROM cypher('g', $$CREATE (:v1)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v1)$$) AS r(a agtype);
a
---
(0 rows)
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+----+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
- v1 | 3 | v | g.v1
+ name | id | kind | relation
+------------------+----+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
+ v1 | 3 | v | graph.v1
(3 rows)
-- skip label id 2 to test the logic that gets an unused label id after cycle
-SELECT nextval('g._label_id_seq');
+SELECT nextval('graph._label_id_seq');
nextval
---------
4
(1 row)
-- label id is now 3
-SELECT * FROM cypher('g', $$CREATE (:v3)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v3)$$) as r(a agtype);
a
---
(0 rows)
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+----+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
- v1 | 3 | v | g.v1
- v3 | 5 | v | g.v3
+ name | id | kind | relation
+------------------+----+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
+ v1 | 3 | v | graph.v1
+ v3 | 5 | v | graph.v3
(4 rows)
-- to use 65535 as the next label id, set label id to 65534
-SELECT setval('g._label_id_seq', 65534);
+SELECT setval('graph._label_id_seq', 65534);
setval
--------
65534
(1 row)
-- label id is now 65535
-SELECT * FROM cypher('g', $$CREATE (:v65535)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v65535)$$) as r(a agtype);
a
---
(0 rows)
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+-------+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
- v1 | 3 | v | g.v1
- v3 | 5 | v | g.v3
- v65535 | 65535 | v | g.v65535
+ name | id | kind | relation
+------------------+-------+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
+ v1 | 3 | v | graph.v1
+ v3 | 5 | v | graph.v3
+ v65535 | 65535 | v | graph.v65535
(5 rows)
-- after cycle, label id is now 2
-SELECT * FROM cypher('g', $$CREATE (:v2)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v2)$$) as r(a agtype);
a
---
(0 rows)
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+-------+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
- v1 | 3 | v | g.v1
- v3 | 5 | v | g.v3
- v65535 | 65535 | v | g.v65535
- v2 | 4 | v | g.v2
+ name | id | kind | relation
+------------------+-------+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
+ v1 | 3 | v | graph.v1
+ v3 | 5 | v | graph.v3
+ v65535 | 65535 | v | graph.v65535
+ v2 | 4 | v | graph.v2
(6 rows)
-SELECT drop_graph('g', true);
+SELECT drop_graph('graph', true);
NOTICE: drop cascades to 6 other objects
-DETAIL: drop cascades to table g._ag_label_vertex
-drop cascades to table g._ag_label_edge
-drop cascades to table g.v1
-drop cascades to table g.v3
-drop cascades to table g.v65535
-drop cascades to table g.v2
-NOTICE: graph "g" has been dropped
+DETAIL: drop cascades to table graph._ag_label_vertex
+drop cascades to table graph._ag_label_edge
+drop cascades to table graph.v1
+drop cascades to table graph.v3
+drop cascades to table graph.v65535
+drop cascades to table graph.v2
+NOTICE: graph "graph" has been dropped
drop_graph
------------
(1 row)
-- create labels
-SELECT create_graph('g');
-NOTICE: graph "g" has been created
+SELECT create_graph('graph');
+NOTICE: graph "graph" has been created
create_graph
--------------
(1 row)
-SELECT create_vlabel('g', 'n');
+SELECT create_vlabel('graph', 'n');
NOTICE: VLabel "n" has been created
create_vlabel
---------------
(1 row)
-SELECT create_elabel('g', 'r');
+SELECT create_elabel('graph', 'r');
NOTICE: ELabel "r" has been created
create_elabel
---------------
@@ -337,29 +337,29 @@ NOTICE: ELabel "r" has been created
-- check if labels have been created or not
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+----+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
- n | 3 | v | g.n
- r | 4 | e | g.r
+ name | id | kind | relation
+------------------+----+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
+ n | 3 | v | graph.n
+ r | 4 | e | graph.r
(4 rows)
-- try to create duplicate labels
-SELECT create_vlabel('g', 'n');
+SELECT create_vlabel('graph', 'n');
ERROR: label "n" already exists
-SELECT create_elabel('g', 'r');
+SELECT create_elabel('graph', 'r');
ERROR: label "r" already exists
-- remove the labels that have been created
-SELECT drop_label('g', 'n', false);
-NOTICE: label "g"."n" has been dropped
+SELECT drop_label('graph', 'n', false);
+NOTICE: label "graph"."n" has been dropped
drop_label
------------
(1 row)
-SELECT drop_label('g', 'r', false);
-NOTICE: label "g"."r" has been dropped
+SELECT drop_label('graph', 'r', false);
+NOTICE: label "graph"."r" has been dropped
drop_label
------------
@@ -367,21 +367,21 @@ NOTICE: label "g"."r" has been dropped
-- check if labels have been deleted or not
SELECT name, id, kind, relation FROM ag_label;
- name | id | kind | relation
-------------------+----+------+--------------------
- _ag_label_vertex | 1 | v | g._ag_label_vertex
- _ag_label_edge | 2 | e | g._ag_label_edge
+ name | id | kind | relation
+------------------+----+------+------------------------
+ _ag_label_vertex | 1 | v | graph._ag_label_vertex
+ _ag_label_edge | 2 | e | graph._ag_label_edge
(2 rows)
-- try to remove labels that is not there
-SELECT drop_label('g', 'n');
+SELECT drop_label('graph', 'n');
ERROR: label "n" does not exist
-SELECT drop_label('g', 'r');
+SELECT drop_label('graph', 'r');
ERROR: label "r" does not exist
-- Trying to call the functions with label null
-SELECT create_vlabel('g', NULL);
+SELECT create_vlabel('graph', NULL);
ERROR: label name must not be NULL
-SELECT create_elabel('g', NULL);
+SELECT create_elabel('graph', NULL);
ERROR: label name must not be NULL
-- Trying to call the functions with graph null
SELECT create_vlabel(NULL, 'n');
@@ -394,11 +394,11 @@ ERROR: graph name must not be NULL
SELECT create_elabel(NULL, NULL);
ERROR: graph name must not be NULL
-- dropping the graph
-SELECT drop_graph('g', true);
+SELECT drop_graph('graph', true);
NOTICE: drop cascades to 2 other objects
-DETAIL: drop cascades to table g._ag_label_vertex
-drop cascades to table g._ag_label_edge
-NOTICE: graph "g" has been dropped
+DETAIL: drop cascades to table graph._ag_label_vertex
+drop cascades to table graph._ag_label_edge
+NOTICE: graph "graph" has been dropped
drop_graph
------------
diff --git a/regress/expected/name_validation.out
b/regress/expected/name_validation.out
new file mode 100644
index 0000000..a1bc779
--- /dev/null
+++ b/regress/expected/name_validation.out
@@ -0,0 +1,401 @@
+/*
+ * 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.
+ */
+LOAD 'age';
+SET search_path TO ag_catalog;
+--
+-- Test graph names
+--
+-- length
+-- invalid (length < 3)
+SELECT create_graph('db');
+ERROR: graph name is invalid
+-- valid (though length > 63, it's truncated automatically before reaching
validation function)
+SELECT
create_graph('oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsdss');
+NOTICE: graph
"oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds" has been
created
+ create_graph
+--------------
+
+(1 row)
+
+-- valid
+SELECT create_graph('mydatabase');
+NOTICE: graph "mydatabase" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- numeric characters
+-- invalid (first character numeric; only alphabetic allowed)
+SELECT create_graph('2mydatabase');
+ERROR: graph name is invalid
+-- valid
+SELECT create_graph('mydatabase2');
+NOTICE: graph "mydatabase2" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- special characters
+-- invalid (newline character)
+SELECT create_graph('my
+database');
+ERROR: graph name is invalid
+-- invalid (space character)
+SELECT create_graph('my database');
+ERROR: graph name is invalid
+-- invalid (symbol character)
+SELECT create_graph('my&database');
+ERROR: graph name is invalid
+-- valid (non-ascii alphabet)
+SELECT create_graph('mydätabase');
+NOTICE: graph "mydätabase" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT create_graph('mydঅtabase');
+NOTICE: graph "mydঅtabase" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- dots, dashes, underscore
+-- valid
+SELECT create_graph('main.db');
+NOTICE: graph "main.db" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- invalid (ends with dot)
+SELECT create_graph('main.db.');
+ERROR: graph name is invalid
+-- valid
+SELECT create_graph('main-db');
+NOTICE: graph "main-db" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- invalid (ends with dash)
+SELECT create_graph('main.db-');
+ERROR: graph name is invalid
+-- valid
+SELECT create_graph('_mydatabase');
+NOTICE: graph "_mydatabase" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT create_graph('my_database');
+NOTICE: graph "my_database" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- test rename
+-- invalid
+SELECT alter_graph('mydatabase', 'RENAME', '1mydatabase');
+ERROR: new graph name is invalid
+-- valid
+SELECT alter_graph('mydatabase', 'RENAME', 'mydatabase1');
+NOTICE: graph "mydatabase" renamed to "mydatabase1"
+ alter_graph
+-------------
+
+(1 row)
+
+-- clean up
+SELECT drop_graph('mydatabase1', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table mydatabase1._ag_label_vertex
+drop cascades to table mydatabase1._ag_label_edge
+NOTICE: graph "mydatabase1" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('mydätabase', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table "mydätabase"._ag_label_vertex
+drop cascades to table "mydätabase"._ag_label_edge
+NOTICE: graph "mydätabase" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('mydঅtabase', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table "mydঅtabase"._ag_label_vertex
+drop cascades to table "mydঅtabase"._ag_label_edge
+NOTICE: graph "mydঅtabase" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('mydatabase2', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table mydatabase2._ag_label_vertex
+drop cascades to table mydatabase2._ag_label_edge
+NOTICE: graph "mydatabase2" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('main.db', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table "main.db"._ag_label_vertex
+drop cascades to table "main.db"._ag_label_edge
+NOTICE: graph "main.db" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('main-db', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table "main-db"._ag_label_vertex
+drop cascades to table "main-db"._ag_label_edge
+NOTICE: graph "main-db" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('_mydatabase', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table _mydatabase._ag_label_vertex
+drop cascades to table _mydatabase._ag_label_edge
+NOTICE: graph "_mydatabase" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('my_database', true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table my_database._ag_label_vertex
+drop cascades to table my_database._ag_label_edge
+NOTICE: graph "my_database" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT
drop_graph('oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds',
true);
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table
oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds._ag_label_vertex
+drop cascades to table
oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds._ag_label_edge
+NOTICE: graph
"oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds" has been
dropped
+ drop_graph
+------------
+
+(1 row)
+
+--
+-- Test label names
+--
+SELECT create_graph('graph123');
+NOTICE: graph "graph123" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- length
+-- invalid
+SELECT create_vlabel('graph123', '');
+ERROR: label name is invalid
+SELECT create_elabel('graph123', '');
+ERROR: label name is invalid
+-- valid
+SELECT create_vlabel('graph123', 'labelx');
+NOTICE: VLabel "labelx" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', 'labely');
+NOTICE: ELabel "labely" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+-- special characters
+-- invalid (newline character)
+SELECT create_vlabel('graph123', 'my
+label');
+ERROR: label name is invalid
+SELECT create_elabel('graph123', 'my
+label');
+ERROR: label name is invalid
+-- invalid (space character)
+SELECT create_vlabel('graph123', 'my label');
+ERROR: label name is invalid
+SELECT create_elabel('graph123', 'my label');
+ERROR: label name is invalid
+-- invalid (symbol character)
+SELECT create_vlabel('graph123', 'my&label');
+ERROR: label name is invalid
+SELECT create_elabel('graph123', 'my&label');
+ERROR: label name is invalid
+-- valid (non-ascii alphabet)
+SELECT create_vlabel('graph123', 'myläbelx');
+NOTICE: VLabel "myläbelx" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', 'myläbely');
+NOTICE: ELabel "myläbely" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+SELECT create_vlabel('graph123', 'mylঅbelx');
+NOTICE: VLabel "mylঅbelx" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', 'mylঅbely');
+NOTICE: ELabel "mylঅbely" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+-- valid (underscore)
+SELECT create_vlabel('graph123', '_labelx');
+NOTICE: VLabel "_labelx" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', '_labely');
+NOTICE: ELabel "_labely" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+SELECT create_vlabel('graph123', 'label_x');
+NOTICE: VLabel "label_x" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', 'label_y');
+NOTICE: ELabel "label_y" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+-- numeric
+-- invalid
+SELECT create_vlabel('graph123', '1label');
+ERROR: label name is invalid
+SELECT create_elabel('graph123', '2label');
+ERROR: label name is invalid
+-- valid
+SELECT create_vlabel('graph123', 'label1');
+NOTICE: VLabel "label1" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT create_elabel('graph123', 'label2');
+NOTICE: ELabel "label2" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+-- label creation with cypher
+-- invalid
+SELECT * from cypher('graph123', $$ CREATE (a:`my&label`) $$) as (a agtype);
+ERROR: label name is invalid
+LINE 1: SELECT * from cypher('graph123', $$ CREATE (a:`my&label`) $$...
+ ^
+SELECT * from cypher('graph123', $$ CREATE (:A)-[:`my&label2`]->(:C) $$) as (a
agtype);
+ERROR: label name is invalid
+LINE 1: SELECT * from cypher('graph123', $$ CREATE (:A)-[:`my&label2...
+ ^
+-- valid
+SELECT * from cypher('graph123', $$ CREATE (a:`mylabel`) $$) as (a agtype);
+ a
+---
+(0 rows)
+
+SELECT * from cypher('graph123', $$ CREATE (:A)-[:`mylabel2`]->(:C) $$) as (a
agtype);
+ a
+---
+(0 rows)
+
+-- clean up
+SELECT drop_graph('graph123', true);
+NOTICE: drop cascades to 18 other objects
+DETAIL: drop cascades to table graph123._ag_label_vertex
+drop cascades to table graph123._ag_label_edge
+drop cascades to table graph123.labelx
+drop cascades to table graph123.labely
+drop cascades to table graph123."myläbelx"
+drop cascades to table graph123."myläbely"
+drop cascades to table graph123."mylঅbelx"
+drop cascades to table graph123."mylঅbely"
+drop cascades to table graph123._labelx
+drop cascades to table graph123._labely
+drop cascades to table graph123.label_x
+drop cascades to table graph123.label_y
+drop cascades to table graph123.label1
+drop cascades to table graph123.label2
+drop cascades to table graph123.mylabel
+drop cascades to table graph123."A"
+drop cascades to table graph123.mylabel2
+drop cascades to table graph123."C"
+NOTICE: graph "graph123" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+--
+-- End of test
+--
diff --git a/regress/sql/catalog.sql b/regress/sql/catalog.sql
index 59a720f..363ac66 100644
--- a/regress/sql/catalog.sql
+++ b/regress/sql/catalog.sql
@@ -24,30 +24,30 @@ SET search_path TO ag_catalog;
-- create_graph(), drop_label(), and drop_graph() tests
--
-SELECT create_graph('g');
-SELECT * FROM ag_graph WHERE name = 'g';
+SELECT create_graph('graph');
+SELECT * FROM ag_graph WHERE name = 'graph';
-- create a label to test drop_label()
-SELECT * FROM cypher('g', $$CREATE (:l)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:l)$$) AS r(a agtype);
-- test drop_label()
-SELECT drop_label('g', 'l');
+SELECT drop_label('graph', 'l');
-- create a label to test drop_graph()
-SELECT * FROM cypher('g', $$CREATE (:v)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v)$$) AS r(a agtype);
-- DROP SCHEMA ... CASCADE should fail
-DROP SCHEMA g CASCADE;
+DROP SCHEMA graph CASCADE;
-- DROP TABLE ... should fail
-DROP TABLE g.v;
+DROP TABLE graph.v;
-- should fail (cascade = false)
-SELECT drop_graph('g');
+SELECT drop_graph('graph');
-SELECT drop_graph('g', true);
-SELECT count(*) FROM ag_graph WHERE name = 'g';
-SELECT count(*) FROM pg_namespace WHERE nspname = 'g';
+SELECT drop_graph('graph', true);
+SELECT count(*) FROM ag_graph WHERE name = 'graph';
+SELECT count(*) FROM pg_namespace WHERE nspname = 'graph';
-- invalid cases
SELECT create_graph(NULL);
@@ -104,59 +104,59 @@ SELECT alter_graph('GraphB', 'DUMMY', 'GraphA');
-- label id test
--
-SELECT create_graph('g');
+SELECT create_graph('graph');
-- label id starts from 1
-SELECT * FROM cypher('g', $$CREATE (:v1)$$) AS r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v1)$$) AS r(a agtype);
SELECT name, id, kind, relation FROM ag_label;
-- skip label id 2 to test the logic that gets an unused label id after cycle
-SELECT nextval('g._label_id_seq');
+SELECT nextval('graph._label_id_seq');
-- label id is now 3
-SELECT * FROM cypher('g', $$CREATE (:v3)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v3)$$) as r(a agtype);
SELECT name, id, kind, relation FROM ag_label;
-- to use 65535 as the next label id, set label id to 65534
-SELECT setval('g._label_id_seq', 65534);
+SELECT setval('graph._label_id_seq', 65534);
-- label id is now 65535
-SELECT * FROM cypher('g', $$CREATE (:v65535)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v65535)$$) as r(a agtype);
SELECT name, id, kind, relation FROM ag_label;
-- after cycle, label id is now 2
-SELECT * FROM cypher('g', $$CREATE (:v2)$$) as r(a agtype);
+SELECT * FROM cypher('graph', $$CREATE (:v2)$$) as r(a agtype);
SELECT name, id, kind, relation FROM ag_label;
-SELECT drop_graph('g', true);
+SELECT drop_graph('graph', true);
-- create labels
-SELECT create_graph('g');
-SELECT create_vlabel('g', 'n');
-SELECT create_elabel('g', 'r');
+SELECT create_graph('graph');
+SELECT create_vlabel('graph', 'n');
+SELECT create_elabel('graph', 'r');
-- check if labels have been created or not
SELECT name, id, kind, relation FROM ag_label;
-- try to create duplicate labels
-SELECT create_vlabel('g', 'n');
-SELECT create_elabel('g', 'r');
+SELECT create_vlabel('graph', 'n');
+SELECT create_elabel('graph', 'r');
-- remove the labels that have been created
-SELECT drop_label('g', 'n', false);
-SELECT drop_label('g', 'r', false);
+SELECT drop_label('graph', 'n', false);
+SELECT drop_label('graph', 'r', false);
-- check if labels have been deleted or not
SELECT name, id, kind, relation FROM ag_label;
-- try to remove labels that is not there
-SELECT drop_label('g', 'n');
-SELECT drop_label('g', 'r');
+SELECT drop_label('graph', 'n');
+SELECT drop_label('graph', 'r');
-- Trying to call the functions with label null
-SELECT create_vlabel('g', NULL);
-SELECT create_elabel('g', NULL);
+SELECT create_vlabel('graph', NULL);
+SELECT create_elabel('graph', NULL);
-- Trying to call the functions with graph null
SELECT create_vlabel(NULL, 'n');
@@ -167,6 +167,6 @@ SELECT create_vlabel(NULL, NULL);
SELECT create_elabel(NULL, NULL);
-- dropping the graph
-SELECT drop_graph('g', true);
+SELECT drop_graph('graph', true);
diff --git a/regress/sql/name_validation.sql b/regress/sql/name_validation.sql
new file mode 100644
index 0000000..b67d113
--- /dev/null
+++ b/regress/sql/name_validation.sql
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+LOAD 'age';
+
+SET search_path TO ag_catalog;
+
+--
+-- Test graph names
+--
+
+-- length
+
+-- invalid (length < 3)
+SELECT create_graph('db');
+-- valid (though length > 63, it's truncated automatically before reaching
validation function)
+SELECT
create_graph('oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsdss');
+-- valid
+SELECT create_graph('mydatabase');
+
+-- numeric characters
+
+-- invalid (first character numeric; only alphabetic allowed)
+SELECT create_graph('2mydatabase');
+-- valid
+SELECT create_graph('mydatabase2');
+
+-- special characters
+
+-- invalid (newline character)
+SELECT create_graph('my
+database');
+-- invalid (space character)
+SELECT create_graph('my database');
+-- invalid (symbol character)
+SELECT create_graph('my&database');
+-- valid (non-ascii alphabet)
+SELECT create_graph('mydätabase');
+SELECT create_graph('mydঅtabase');
+
+-- dots, dashes, underscore
+
+-- valid
+SELECT create_graph('main.db');
+-- invalid (ends with dot)
+SELECT create_graph('main.db.');
+-- valid
+SELECT create_graph('main-db');
+-- invalid (ends with dash)
+SELECT create_graph('main.db-');
+-- valid
+SELECT create_graph('_mydatabase');
+SELECT create_graph('my_database');
+
+-- test rename
+
+-- invalid
+SELECT alter_graph('mydatabase', 'RENAME', '1mydatabase');
+-- valid
+SELECT alter_graph('mydatabase', 'RENAME', 'mydatabase1');
+
+-- clean up
+SELECT drop_graph('mydatabase1', true);
+SELECT drop_graph('mydätabase', true);
+SELECT drop_graph('mydঅtabase', true);
+SELECT drop_graph('mydatabase2', true);
+SELECT drop_graph('main.db', true);
+SELECT drop_graph('main-db', true);
+SELECT drop_graph('_mydatabase', true);
+SELECT drop_graph('my_database', true);
+SELECT
drop_graph('oiblpsacrufgxiilyevvoiblpsacrufgxiilyevvoiblpsacrufgxiilyevvsds',
true);
+
+
+--
+-- Test label names
+--
+
+SELECT create_graph('graph123');
+
+-- length
+
+-- invalid
+SELECT create_vlabel('graph123', '');
+SELECT create_elabel('graph123', '');
+-- valid
+SELECT create_vlabel('graph123', 'labelx');
+SELECT create_elabel('graph123', 'labely');
+
+-- special characters
+
+-- invalid (newline character)
+SELECT create_vlabel('graph123', 'my
+label');
+SELECT create_elabel('graph123', 'my
+label');
+-- invalid (space character)
+SELECT create_vlabel('graph123', 'my label');
+SELECT create_elabel('graph123', 'my label');
+-- invalid (symbol character)
+SELECT create_vlabel('graph123', 'my&label');
+SELECT create_elabel('graph123', 'my&label');
+-- valid (non-ascii alphabet)
+SELECT create_vlabel('graph123', 'myläbelx');
+SELECT create_elabel('graph123', 'myläbely');
+SELECT create_vlabel('graph123', 'mylঅbelx');
+SELECT create_elabel('graph123', 'mylঅbely');
+-- valid (underscore)
+SELECT create_vlabel('graph123', '_labelx');
+SELECT create_elabel('graph123', '_labely');
+SELECT create_vlabel('graph123', 'label_x');
+SELECT create_elabel('graph123', 'label_y');
+
+-- numeric
+
+-- invalid
+SELECT create_vlabel('graph123', '1label');
+SELECT create_elabel('graph123', '2label');
+-- valid
+SELECT create_vlabel('graph123', 'label1');
+SELECT create_elabel('graph123', 'label2');
+
+-- label creation with cypher
+
+-- invalid
+SELECT * from cypher('graph123', $$ CREATE (a:`my&label`) $$) as (a agtype);
+SELECT * from cypher('graph123', $$ CREATE (:A)-[:`my&label2`]->(:C) $$) as (a
agtype);
+
+-- valid
+SELECT * from cypher('graph123', $$ CREATE (a:`mylabel`) $$) as (a agtype);
+SELECT * from cypher('graph123', $$ CREATE (:A)-[:`mylabel2`]->(:C) $$) as (a
agtype);
+
+-- clean up
+SELECT drop_graph('graph123', true);
+
+--
+-- End of test
+--
diff --git a/src/backend/commands/graph_commands.c
b/src/backend/commands/graph_commands.c
index bb21f8a..f0074ed 100644
--- a/src/backend/commands/graph_commands.c
+++ b/src/backend/commands/graph_commands.c
@@ -43,6 +43,7 @@
#include "catalog/ag_label.h"
#include "commands/label_commands.h"
#include "utils/graphid.h"
+#include "utils/name_validation.h"
/*
* Schema name doesn't have to be graph name but the same name is used so
@@ -73,17 +74,16 @@ Datum create_graph(PG_FUNCTION_ARGS)
graph_name_str = NameStr(*graph_name);
- if (strlen(graph_name_str) == 0)
+ if (!is_valid_graph_name(graph_name_str))
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("graph name can not be empty")));
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("graph name is invalid")));
}
if (graph_exists(graph_name_str))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
- errmsg("graph \"%s\" already exists",
graph_name_str)));
+ errmsg("graph \"%s\" already exists", graph_name_str)));
}
nsp_id = create_schema_for_graph(graph_name);
@@ -323,6 +323,12 @@ static void rename_graph(const Name graph_name, const Name
new_name)
char *newname = NameStr(*new_name);
char *schema_name;
+ if (!is_valid_graph_name(newname))
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("new graph name is invalid")));
+ }
+
/*
* ProcessUtilityContext of this command is PROCESS_UTILITY_SUBCOMMAND
* so the event trigger will not be fired.
diff --git a/src/backend/commands/label_commands.c
b/src/backend/commands/label_commands.c
index 02bf574..f1a0f9e 100644
--- a/src/backend/commands/label_commands.c
+++ b/src/backend/commands/label_commands.c
@@ -52,6 +52,7 @@
#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
@@ -226,7 +227,7 @@ Datum create_elabel(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
- errmsg("graph \"%s\" does not exist.",
graph_name_str)));
+ errmsg("graph \"%s\" does not exist.", graph_name_str)));
}
graph_oid = get_graph_oid(graph_name_str);
@@ -273,6 +274,12 @@ Oid create_label(char *graph_name, char *label_name, char
label_type,
Oid relation_id;
Oid label_oid;
+ 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)
{
diff --git a/src/backend/utils/name_validation.c
b/src/backend/utils/name_validation.c
new file mode 100644
index 0000000..d96f315
--- /dev/null
+++ b/src/backend/utils/name_validation.c
@@ -0,0 +1,91 @@
+/*
+ * 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 "catalog/pg_collation_d.h"
+#include "utils/builtins.h"
+
+#include "utils/name_validation.h"
+
+static int regex_match(const char *string, const char *pattern);
+
+/*
+ * Returns whether the graph name is valid.
+ *
+ * @param graph_name name of the graph
+ * @return int
+ */
+int is_valid_graph_name(const char *graph_name)
+{
+ int len = strlen(graph_name);
+
+ if (len < MIN_GRAPH_NAME_LEN || len > MAX_GRAPH_NAME_LEN)
+ {
+ return 0;
+ }
+
+ return regex_match(graph_name, REGEX_GRAPH_NAME);
+}
+
+/*
+ * Returns whether the label name is valid.
+ *
+ * Note: label_type parameter is not used in this implementation.
+ * It should be used if validation algorithm for edge and vetex
+ * differs in future.
+ *
+ * @param label_name name of the label
+ * @param label_type label type defined in label_commands.h
+ * @return int
+ */
+int is_valid_label(char *label_name, char label_type)
+{
+ int len = strlen(label_name);
+
+ if (len < MIN_LABEL_NAME_LEN || len > MAX_LABEL_NAME_LEN)
+ {
+ return 0;
+ }
+
+ return regex_match(label_name, REGEX_LABEL_NAME);
+}
+
+/*
+ * Returns whether there is a regex match.
+ *
+ * @param string source string
+ * @param pattern regex pattern
+ * @return int match returns non-zero
+ */
+static int regex_match(const char *string, const char *pattern)
+{
+ text *t_string = NULL;
+ text *t_pattern = NULL;
+ Datum result;
+
+ t_string = cstring_to_text_with_len(string, strlen(string));
+ t_pattern = cstring_to_text_with_len(pattern, strlen(pattern));
+
+ result = (DirectFunctionCall2Coll(textregexeq, C_COLLATION_OID,
+ PointerGetDatum(t_string),
+ PointerGetDatum(t_pattern)));
+
+ return DatumGetBool(result);
+}
diff --git a/src/include/utils/name_validation.h
b/src/include/utils/name_validation.h
new file mode 100644
index 0000000..7cfe9bf
--- /dev/null
+++ b/src/include/utils/name_validation.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef AG_NAME_VALIDATION_H
+#define AG_NAME_VALIDATION_H
+
+/*
+ * Following ID_Start and ID_Continue values are taken from:
+ * https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp
+ */
+#define REGEX_ID_STRT \
+ "A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑ ˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-Ρ Σ-ϵϷ-ҁҊ-ԯԱ-Ֆՙՠ-ֈא-תׯ-ײ
ؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯ ݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࡠ-ࡪ ࡰ-ࢇࢉ-ࢎࢠ-ࣉऄ-हऽॐक़-ॡॱ-ঀঅ-ঌ
এঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱ ৼਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜ ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હ
ઽૐૠૡૹଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହ ଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞ டணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హ
ఽౘ-ౚౝౠౡಀಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ ಽೝೞೠೡೱೲഄ-ഌഎ-ഐഒ-ഺഽൎൔ-ൖ ൟ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะ
าำเ-ๆກຂຄຆ-ຊຌ-ຣລວ-ະາຳຽ ເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ ၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍ
ა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍ ነ-ኰኲ-ኵኸ-ኾዀ
ዂ-ዅወ-ዖዘ-ጐጒ-ጕ ጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚ ᚠ-ᛪᛮ-ᛸᜀ-ᜑᜟ-ᜱᝀ- [...]
+#define REGEX_ID_CONT \
+
"0-9A-Z_a-zªµ·ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮ̀-ʹͶͷͺ-ͽͿΆ-ΊΌΎ-ΡΣ-ϵϷ-ҁ҃-҇Ҋ-ԯԱ-Ֆՙՠ-ֈ֑-ׇֽֿׁׂׅׄא-תׯ-ײؐ-ؚؠ-٩ٮ-ۓە-ۜ۟-۪ۨ-ۼۿܐ-݊ݍ-ޱ߀-ߵߺ߽ࠀ-࠭ࡀ-࡛ࡠ-ࡪࡰ-ࢇࢉ-ࢎ࢘-ࣣ࣡-ॣ०-९ॱ-ঃঅ-ঌএঐও-নপ-রলশ-হ়-ৄেৈো-ৎৗড়ঢ়য়-ৣ০-ৱৼ৾ਁ-ਃਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹ਼ਾ-ੂੇੈੋ-੍ੑਖ਼-ੜਫ਼੦-ੵઁ-ઃઅ-ઍએ-ઑઓ-નપ-રલળવ-હ઼-ૅે-ૉો-્ૐૠ-ૣ૦-૯ૹ-૿ଁ-ଃଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହ଼-ୄେୈୋ-୍୕-ୗଡ଼ଢ଼ୟ-ୣ୦-୯ୱஂஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹா-ூெ-ைொ-்ௐௗ௦-௯ఀ-ఌఎ-ఐఒ-నప-హ఼-ౄె-ైొ-్ౕౖౘ-ౚౝౠ-ౣ౦-౯ಀ-ಃಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ಼-ೄೆ-ೈೊ-್ೕೖೝೞೠ-ೣ೦-೯ೱ-ೳഀ-ഌഎ-ഐഒ-ൄെ-ൈൊ-ൎൔ-ൗൟ-ൣ൦-൯ൺ-ൿඁ-ඃඅ-ඖක-නඳ-රලව-ෆ�
��ා-ුූෘ-ෟ෦-෯ෲෳก-ฺเ-๎๐-๙ກຂຄຆ-ຊຌ-ຣລວ-ຽເ-ໄໆ່-໎໐-໙ໜ [...]
+
+#define REGEX_GRAPH_NAME \
+ "^[" REGEX_ID_STRT "_][" REGEX_ID_CONT "\\.\\-]*[" REGEX_ID_CONT "]$"
+#define REGEX_LABEL_NAME "^[" REGEX_ID_STRT "_][" REGEX_ID_CONT "]*$"
+
+#define MAX_GRAPH_NAME_LEN 63
+#define MIN_GRAPH_NAME_LEN 3
+#define MAX_LABEL_NAME_LEN 65535
+#define MIN_LABEL_NAME_LEN 1
+
+int is_valid_graph_name(const char *graph_name);
+int is_valid_label(char *label_name, char label_type);
+
+#endif