Files
openGauss-server/src/test/regress/sql/rowlevelsecurity.sql
dengxuyue 1567043064 同步source code
日期: 12-26
    revision: ee5b054c
2020-12-28 22:19:21 +08:00

431 lines
19 KiB
PL/PgSQL

--
-- Test for Row Level Security feature
--
-- initial setup
CREATE USER regress_rls_alice NOLOGIN PASSWORD 'Ttest@123';
CREATE USER regress_rls_bob NOLOGIN PASSWORD 'Ttest@123';
CREATE USER regress_rls_david NOLOGIN PASSWORD 'Ttest@123';
CREATE USER regress_rls_peter NOLOGIN PASSWORD 'Ttest@123';
CREATE USER regress_rls_single_user NOLOGIN PASSWORD 'Ttest@123';
CREATE USER regress_rls_admin SYSADMIN NOLOGIN PASSWORD "Ttest@123";
CREATE ROLE regress_rls_group1 NOLOGIN PASSWORD 'Ttest@123';
CREATE ROLE regress_rls_group2 NOLOGIN PASSWORD 'Ttest@123';
GRANT ALL on pg_roles to public;
GRANT ALL on pg_user to public;
GRANT regress_rls_group1 TO regress_rls_alice, regress_rls_bob, regress_rls_peter;
GRANT regress_rls_group2 TO regress_rls_david, regress_rls_peter, regress_rls_admin;
CREATE SCHEMA regress_rls_schema;
GRANT CREATE ON SCHEMA regress_rls_schema to public;
GRANT USAGE ON SCHEMA regress_rls_schema to public;
ALTER DATABASE regression ENABLE PRIVATE OBJECT;
-- reconnect
\c
SET search_path = regress_rls_schema;
-- regress_rls_alice is the owner of all schema
SET ROLE regress_rls_alice PASSWORD 'Ttest@123';
-- setup of malicious function (NOT SHIPPABLE)
CREATE OR REPLACE FUNCTION regress_rls_schema.rls_fleak1(text) RETURNS bool
COST 0.0000001 LANGUAGE plpgsql
AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END';
GRANT EXECUTE ON FUNCTION regress_rls_schema.rls_fleak1(text) TO public;
-- setup of malicious immutable function (SHIPPABLE)
CREATE OR REPLACE FUNCTION regress_rls_schema.rls_fleak2(text) RETURNS bool
COST 0.0000001 LANGUAGE plpgsql IMMUTABLE
AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END';
GRANT EXECUTE ON FUNCTION regress_rls_schema.rls_fleak2(text) TO public;
-- auto generate row level security policy
CREATE OR REPLACE FUNCTION regress_rls_schema.rls_auto_create_policy(t_name text, p_num int)
RETURNS INTEGER AS $$
DECLARE
counter INTEGER := 1;
query text;
BEGIN
WHILE counter <= p_num LOOP
query := 'CREATE ROW LEVEL SECURITY POLICY ' || t_name || '_rls_' || counter || ' ON ' || t_name || ' USING(TRUE);';
EXECUTE query;
counter := counter + 1;
END LOOP;
RETURN 1;
END;
$$language plpgsql;
REVOKE EXECUTE ON FUNCTION regress_rls_schema.rls_auto_create_policy(text, int) FROM public;
-- auto drop row level security policy
CREATE OR REPLACE FUNCTION regress_rls_schema.rls_auto_drop_policy(t_name text, p_num int) RETURNS INTEGER AS $$
DECLARE
counter INTEGER := 1;
query text;
BEGIN
WHILE counter <= p_num LOOP
query := 'DROP ROW LEVEL SECURITY POLICY ' || t_name || '_rls_' || counter || ' ON ' || t_name;
EXECUTE query;
counter := counter + 1;
END LOOP;
RETURN 1;
END;
$$language plpgsql;
REVOKE EXECUTE ON FUNCTION regress_rls_schema.rls_auto_drop_policy(text, int) FROM public;
-- BASIC Row-Level Security Scenario
CREATE TABLE regress_rls_schema.account_row(
aid int,
aname varchar(100)
) WITH (ORIENTATION=row);
GRANT SELECT ON regress_rls_schema.account_row TO public;
INSERT INTO regress_rls_schema.account_row VALUES
(1, 'regress_rls_alice'),
(2, 'regress_rls_bob'),
(3, 'regress_rls_david'),
(4, 'regress_rls_peter'),
(5, 'regress_rls_admin'),
(6, 'regress_rls_single_user');
ANALYZE regress_rls_schema.account_row;
CREATE TABLE regress_rls_schema.account_col(
aid int,
aname varchar(100)
) WITH (ORIENTATION=column);
GRANT SELECT ON regress_rls_schema.account_col TO public;
INSERT INTO regress_rls_schema.account_col SELECT * FROM regress_rls_schema.account_row;
ANALYZE regress_rls_schema.account_col;
CREATE TABLE regress_rls_schema.category_row(
cid int primary key,
cname text
) WITH (ORIENTATION=row);
GRANT ALL ON regress_rls_schema.category_row TO public;
INSERT INTO regress_rls_schema.category_row VALUES
(11, 'novel'),
(22, 'science fiction'),
(33, 'technology'),
(44, 'manga'),
(55, 'biography');
ANALYZE regress_rls_schema.category_row;
CREATE TABLE regress_rls_schema.category_col(
cid int,
cname text
) WITH (ORIENTATION=column);
GRANT ALL ON regress_rls_schema.category_col TO public;
INSERT INTO regress_rls_schema.category_col SELECT * FROM regress_rls_schema.category_row;
ANALYZE regress_rls_schema.category_col;
CREATE TABLE regress_rls_schema.document_row(
did int primary key,
cid int,
dlevel int not null,
dauthor name,
dtitle text
);
GRANT ALL ON regress_rls_schema.document_row TO public;
INSERT INTO regress_rls_schema.document_row VALUES
( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
( 2, 11, 5, 'regress_rls_bob', 'my second novel'),
( 3, 22, 7, 'regress_rls_bob', 'my science fiction'),
( 4, 44, 9, 'regress_rls_bob', 'my first manga'),
( 5, 44, 3, 'regress_rls_bob', 'my second manga'),
( 6, 22, 2, 'regress_rls_peter', 'great science fiction'),
( 7, 33, 6, 'regress_rls_peter', 'great technology book'),
( 8, 44, 4, 'regress_rls_peter', 'great manga'),
( 9, 22, 5, 'regress_rls_david', 'awesome science fiction'),
(10, 33, 4, 'regress_rls_david', 'awesome technology book'),
(11, 55, 8, 'regress_rls_alice', 'great biography'),
(12, 33, 10, 'regress_rls_admin', 'physical technology'),
(13, 55, 5, 'regress_rls_single_user', 'Beethoven biography');
ANALYZE regress_rls_schema.document_row;
CREATE TABLE regress_rls_schema.document_col(
did int,
cid int,
dlevel int not null,
dauthor name,
dtitle text
);
GRANT ALL ON regress_rls_schema.document_col TO public;
INSERT INTO regress_rls_schema.document_col SELECT * FROM regress_rls_schema.document_row;
ANALYZE regress_rls_schema.document_col;
-- create partition table
CREATE TABLE par_row_t1 (id int, a int, b text)partition by range (a)
(
partition par_row_t1_p0 values less than(10),
partition par_row_t1_p1 values less than(50),
partition par_row_t1_p2 values less than(100),
partition par_row_t1_p3 values less than (maxvalue)
);
CREATE TABLE par_col_t1(id int, a int, b text) with(orientation = column) /*distribute by hash (id)*/ PARTITION BY RANGE (a)
(
partition par_col_t1_p0 values less than(10),
partition par_col_t1_p1 values less than(50),
partition par_col_t1_p2 values less than(100),
partition par_col_t1_p3 values less than (maxvalue)
);
INSERT INTO par_row_t1 VALUES (generate_series(1, 150) % 24, generate_series(1, 150), 'huawei');
INSERT INTO par_col_t1 VALUES (generate_series(1, 150) % 24, generate_series(1, 150), 'huawei');
GRANT SELECT ON par_row_t1 TO PUBLIC;
GRANT SELECT ON par_col_t1 TO PUBLIC;
CREATE ROW LEVEL SECURITY POLICY par_row_t1_rls1 ON par_row_t1 AS PERMISSIVE TO public USING(a <= 20);
CREATE ROW LEVEL SECURITY POLICY par_row_t1_rls2 ON par_row_t1 AS RESTRICTIVE TO regress_rls_group2 USING(id < 30);
CREATE ROW LEVEL SECURITY POLICY par_col_t1_rls1 ON par_col_t1 AS PERMISSIVE TO public USING(a <= 20);
CREATE ROW LEVEL SECURITY POLICY par_col_t1_rls2 ON par_col_t1 AS RESTRICTIVE TO regress_rls_group2 USING(id < 30);
ALTER TABLE par_row_t1 ENABLE ROW LEVEL SECURITY;
ALTER TABLE par_col_t1 ENABLE ROW LEVEL SECURITY;
-- create replication table
CREATE TABLE tt_rep(id int, name varchar(100)) /*DISTRIBUTE BY REPLICATION*/;
GRANT SELECT ON tt_rep TO PUBLIC;
INSERT INTO tt_rep VALUES (1, 'regress_rls_alice'), (2, 'regress_rls_david'), (3, 'regress_rls_peter'), (4, 'regress_rls_bob');
ALTER TABLE tt_rep ENABLE ROW LEVEL SECURITY;
CREATE ROW LEVEL SECURITY POLICY tt_rep_rls1 ON tt_rep AS PERMISSIVE FOR SELECT TO regress_rls_group1 USING(name = current_user);
CREATE ROW LEVEL SECURITY POLICY tt_rep_rls2 ON tt_rep AS PERMISSIVE FOR SELECT TO regress_rls_group2 USING(id = 1);
-- create private table, test database private object
CREATE TABLE alice_private(id int, name varchar(100));
CREATE TABLE alice_public_1(id int, name varchar(100));
GRANT SELECT ON alice_public_1 TO regress_rls_group1;
CREATE TABLE alice_public_2(id int, name varchar(100));
GRANT SELECT ON alice_public_2 TO regress_rls_group2;
-- create temp table
CREATE TEMP TABLE temp_tt(id int, name varchar(20));
CREATE ROW LEVEL SECURITY POLICY temp_tt_rls ON temp_tt USING(id < 100);
ALTER TABLE temp_tt ENABLE ROW LEVEL SECURITY;
DROP TABLE temp_tt;
-- create 100 row level security policies on account_row
SELECT regress_rls_schema.rls_auto_create_policy('account_row', 100);
-- create 101 row level security policy on account_row, failed
CREATE ROW LEVEL SECURITY POLICY account_row_rls_101 ON regress_rls_schema.account_row USING(FALSE);
-- drop 100 row level security policies on account_row
SELECT regress_rls_schema.rls_auto_drop_policy('account_row', 100);
-- create row level security policy on account_row, succeed
CREATE ROW LEVEL SECURITY POLICY account_row_rls_101 ON regress_rls_schema.account_row USING(FALSE);
-- drop row level security policy account_row_rls_101 for account_row
DROP ROW LEVEL SECURITY POLICY account_row_rls_101 ON regress_rls_schema.account_row;
SELECT count(*) FROM pg_catalog.pg_rlspolicies where tablename = 'account_row';
-- enable row level security for document_row, document_col
ALTER TABLE regress_rls_schema.document_row ENABLE ROW LEVEL SECURITY;
ALTER TABLE regress_rls_schema.document_col ENABLE ROW LEVEL SECURITY;
-- user's security level must be higher than or equal to document's
CREATE ROW LEVEL SECURITY POLICY p01 ON document_row AS PERMISSIVE
USING (dlevel <= (SELECT aid FROM account_row WHERE aname = current_user));
CREATE ROW LEVEL SECURITY POLICY p01 ON document_col AS PERMISSIVE
USING (dlevel <= (SELECT aid FROM account_col WHERE aname = current_user));
-- try to create a policy of wrong type
CREATE ROW LEVEL SECURITY POLICY p02 ON document_row AS WHATEVER
USING (dlevel <= (SELECT aid FROM account_row WHERE aname = current_user));
-- regress_rls_david isn't allowed to anything at cid 50 or above
-- this is to make sure that we sort the policies by name first
CREATE ROW LEVEL SECURITY POLICY p02 ON document_row AS RESTRICTIVE TO regress_rls_david
USING (cid < 50);
CREATE ROW LEVEL SECURITY POLICY p02 ON document_col AS RESTRICTIVE TO regress_rls_david
USING (cid < 50);
-- and regress_rls_david isn't allowed to see manga documents
CREATE ROW LEVEL SECURITY POLICY p03 ON document_row AS RESTRICTIVE TO regress_rls_david
USING (cid <> 44);
CREATE ROW LEVEL SECURITY POLICY p03 ON document_col AS RESTRICTIVE TO regress_rls_david
USING (cid <> 44);
-- policy for update/delete
CREATE ROW LEVEL SECURITY POLICY p04 ON document_row AS RESTRICTIVE FOR UPDATE TO regress_rls_bob, regress_rls_david USING ((dlevel % 2) = 1);
CREATE ROW LEVEL SECURITY POLICY p05 ON document_row AS RESTRICTIVE FOR DELETE TO regress_rls_bob, regress_rls_david USING ((dlevel % 2) = 0);
-- policy for regress_rls_bob
CREATE ROW LEVEL SECURITY POLICY p06 ON document_row AS RESTRICTIVE FOR SELECT TO regress_rls_bob USING ((dlevel % 2) = 1);
\d
\d+ document_row
SELECT * FROM pg_rlspolicies WHERE schemaname = 'regress_rls_schema' AND tablename = 'document_row' ORDER BY policyname;
-- prepare statement
PREPARE one AS SELECT * FROM document_row ORDER BY 1;
PREPARE two AS SELECT * FROM document_col ORDER BY 1;
EXECUTE one;
EXECUTE one;
EXECUTE two;
EXECUTE two;
-- viewpoint from regress_rls_bob
SET ROLE regress_rls_bob PASSWORD 'Ttest@123';
EXECUTE one;
EXECUTE two;
SELECT * FROM document_row WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM document_col WHERE rls_fleak2(dauthor) ORDER BY did;
-- EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row WHERE rls_fleak2(dauthor) ORDER BY did;
SELECT * FROM document_col INNER JOIN category_col ON document_col.cid=category_col.cid WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM tt_rep;
SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
-- EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
\d
\df
-- viewpoint from regress_rls_peter
SET ROLE regress_rls_peter PASSWORD 'Ttest@123';
EXECUTE one;
EXECUTE two;
SELECT * FROM document_row WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM document_col WHERE rls_fleak2(dauthor) ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row WHERE rls_fleak2(dauthor) ORDER BY did;
SELECT * FROM document_col INNER JOIN category_col ON document_col.cid=category_col.cid WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM tt_rep;
SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
-- viewpoint from regress_rls_david
SET ROLE regress_rls_david PASSWORD 'Ttest@123';
EXECUTE one;
EXECUTE two;
SELECT * FROM document_row WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM document_col WHERE rls_fleak2(dauthor) ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row WHERE rls_fleak2(dauthor) ORDER BY did;
SELECT * FROM document_col INNER JOIN category_col ON document_col.cid=category_col.cid WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM tt_rep;
COPY document_row TO STDOUT;
COPY document_col TO STDOUT;
SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dauthor) ORDER BY did;
-- update and update returning
UPDATE document_row SET dlevel = dlevel + 1 - 1 WHERE did > 1;
UPDATE document_col SET dlevel = dlevel + 1 - 1 WHERE did > 1 RETURNING dauthor, did;
-- delete and delete returning
INSERT INTO document_row VALUES (100, 49, 1, 'regress_rls_david', 'testing sorting of policies');
DELETE FROM document_row WHERE did = 100;
INSERT INTO document_row VALUES (100, 49, 1, 'regress_rls_david', 'testing sorting of policies');
DELETE FROM document_row WHERE did = 100 RETURNING dauthor, did;
-- only owner can change policies
ALTER POLICY p01 ON document_row USING (true); --fail
DROP POLICY p01 ON document_col; --fail
-- check data from partition table
SELECT * FROM par_row_t1 WHERE a > 7 ORDER BY 1, 2;
SELECT * FROM par_col_t1 WHERE a > 7 ORDER BY 1, 2;
-- test create table as
CREATE TABLE document_row_david AS SELECT * FROM document_row;
SELECT COUNT(*) FROM document_row_david;
-- check table and functions
\d
\df
-- change to super user
RESET ROLE;
-- DROP USER failed, display dependency
DROP USER regress_rls_bob;
DROP OWNED BY regress_rls_bob;
select * from pg_shdepend where classid = 3254 and refclassid = 1260 and refobjid = (select oid from pg_authid where rolname = 'regress_rls_bob');
DROP USER regress_rls_bob;
ALTER POLICY p01 ON document_row USING (dauthor = current_user);
ALTER POLICY p01 ON document_row RENAME TO p12;
ALTER POLICY p12 ON document_row RENAME TO p13;
ALTER POLICY p13 ON document_row RENAME TO p01;
SELECT * FROM pg_rlspolicies ORDER BY tablename, policyname;
-- enable private object
ALTER DATABASE regression DISABLE PRIVATE OBJECT;
-- reconnect
\c
SET search_path = regress_rls_schema;
-- check audit logs
SELECT type, database, object_name, detail_info FROM pg_query_audit('2000-01-01 00:00:00', '2100-01-01 00:00:00')
WHERE detail_info LIKE '%private object%' OR detail_info LIKE '%PRIVATE OBJECT%' ORDER BY detail_info;
-- viewpoint from rls_regres_david again
SET ROLE regress_rls_david PASSWORD 'Ttest@123';
SELECT * FROM document_row ORDER BY did;
SELECT * FROM document_col ORDER BY did;
SELECT * FROM document_row WHERE rls_fleak1(dtitle) ORDER BY did;
SELECT * FROM document_row WHERE rls_fleak2(dtitle) ORDER BY did;
EXPLAIN (COSTS OFF, VERBOSE ON) SELECT * FROM document_row WHERE rls_fleak2(dtitle);
SELECT * FROM document_row INNER JOIN category_row ON document_row.cid=category_row.cid WHERE rls_fleak2(dtitle) ORDER by did;
-- test inlist
SET qrw_inlist2join_optmode=1;
CREATE TABLE inlist_t1(c1 int, c2 int, c3 int) /*DISTRIBUTE BY HASH(c1)*/;
INSERT INTO inlist_t1 SELECT v,v,v FROM generate_series(1,12) as v;
CREATE ROW LEVEL SECURITY POLICY inlist_t1_rls ON inlist_t1 USING(c3 IN (3,4,7));
ALTER TABLE inlist_t1 ENABLE ROW LEVEL SECURITY;
ALTER TABLE inlist_t1 FORCE ROW LEVEL SECURITY;
SELECT * FROM inlist_t1 ORDER BY c1;
RESET qrw_inlist2join_optmode;
-- check data from partition table
SELECT * FROM par_row_t1 WHERE a > 7 ORDER BY 1, 2;
SELECT * FROM par_col_t1 WHERE a > 7 ORDER BY 1, 2;
SELECT * FROM tt_rep;
-- check table and functions
\d
\df
-- viewpoint from regress_rls_alice again
SET ROLE regress_rls_alice PASSWORD 'Ttest@123';
ALTER TABLE tt_rep FORCE ROW LEVEL SECURITY;
ALTER TABLE par_row_t1 FORCE ROW LEVEL SECURITY;
\d
SELECT * FROM tt_rep ORDER BY id;
SELECT * FROM par_row_t1 ORDER BY id;
-- check infinite recursion for rls
CREATE TABLE aa(a int);
CREATE TABLE bb(a int);
ALTER TABLE aa ENABLE ROW LEVEL SECURITY;
ALTER TABLE bb ENABLE ROW LEVEL SECURITY;
CREATE ROW LEVEL SECURITY POLICY aa_rls ON aa USING(EXISTS (SELECT a FROM bb));
-- create failed because of infinite recursion in rls policy
CREATE ROW LEVEL SECURITY POLICY bb_rls ON bb USING(EXISTS (SELECT a FROM aa));
ALTER TABLE aa DISABLE ROW LEVEL SECURITY;
-- create succeed because of aa disable row level security
CREATE ROW LEVEL SECURITY POLICY bb_rls ON bb USING(EXISTS (SELECT a FROM aa));
ALTER TABLE aa ENABLE ROW LEVEL SECURITY;
ALTER TABLE aa FORCE ROW LEVEL SECURITY;
ALTER TABLE bb FORCE ROW LEVEL SECURITY;
-- select failed because of infinite recursion in rls policy
SELECT * FROM aa;
ALTER ROW LEVEL SECURITY POLICY aa_rls ON aa USING(a > 10);
ALTER ROW LEVEL SECURITY POLICY aa_rls ON aa USING(EXISTS (SELECT a FROM bb LIMIT 1));
DROP ROW LEVEL SECURITY POLICY aa_rls ON aa;
CREATE ROW LEVEL SECURITY POLICY aa_rls ON aa AS RESTRICTIVE FOR SELECT TO PUBLIC USING(EXISTS(SELECT a FROM (SELECT a + 100 FROM aa WHERE a > 10 and a < 100 GROUP BY a HAVING count(*) >1)));
DROP TABLE aa CASCADE;
DROP TABLE bb CASCADE;
-- check any sublink
create table aa(aa_1 int, aa_2 int, rls int);
create policy aa_rls on aa using (rls = 1);
alter table aa enable row level security;
alter table aa force row level security;
create table bb(bb_1 int, bb_2 int, rls int);
create policy bb_rls on bb using (rls = 1);
alter table bb enable row level security;
alter table bb force row level security;
explain(costs off) select aa_1 from aa, bb where bb_1 = 1 and aa_1 > (select min(aa_1) from aa where aa_2 = bb_2 and aa_2 = 1);
-- clean environment
RESET ROLE;
DROP ROW LEVEL SECURITY POLICY t12 ON inlist_t1;
DROP ROW LEVEL SECURITY POLICY IF EXISTS t12 ON inlist_t1;
DROP SCHEMA regress_rls_schema CASCADE;
DROP USER IF EXISTS regress_rls_alice;
DROP USER IF EXISTS regress_rls_bob;
DROP USER IF EXISTS regress_rls_david;
DROP USER IF EXISTS regress_rls_peter;
DROP USER IF EXISTS regress_rls_admin;
DROP USER IF EXISTS regress_rls_single_user;
DROP ROLE IF EXISTS regress_rls_group1;
DROP ROLE IF EXISTS regress_rls_group2;
-- check again
SELECT COUNT(*) FROM pg_rlspolicies;
SELECT COUNT(*) FROM pg_depend WHERE classid = 3254 OR refclassid = 3254;
SELECT COUNT(*) FROM pg_shdepend WHERE classid = 3254 OR refclassid = 3254;