Thread: Operator class and index
Maybe this is not right thread to ask this question, and I wrote to pgsql-general too,
but I'll ask anyway.
I have question for someone who is familiar with indexes and index classes.
I tried to write a "begins with" operator to use it instead of LIKE 'xx%',
because LIKE 'xx%' can't use index.
because LIKE 'xx%' can't use index.
These are steps I did:
1. Created function that returns true if "strInput" begins with "strMatch":
CREATE OR REPLACE FUNCTION begins_with(varchar, varchar) RETURNS boolean AS '
DECLARE strInput ALIAS FOR $1;
strMatch ALIAS FOR $2;
nLenMatch INTEGER;
nLenStr INTEGER;
BEGIN
nLenStr=char_length(strInput);
nLenMatch=char_length(strMatch);
IF nLenStr<nLenMatch THEN
RETURN false;
END IF;
IF substr(strInput,1,nLenMatch)=strMatch THEN
RETURN true;
END IF;
RETURN false;
END;' LANGUAGE 'plpgsql';
DECLARE strInput ALIAS FOR $1;
strMatch ALIAS FOR $2;
nLenMatch INTEGER;
nLenStr INTEGER;
BEGIN
nLenStr=char_length(strInput);
nLenMatch=char_length(strMatch);
IF nLenStr<nLenMatch THEN
RETURN false;
END IF;
IF substr(strInput,1,nLenMatch)=strMatch THEN
RETURN true;
END IF;
RETURN false;
END;' LANGUAGE 'plpgsql';
2. Created operator <==
DROP OPERATOR <== (varchar,varchar);
CREATE OPERATOR <==
( leftarg = varchar,
rightarg = varchar,
procedure = begins_with , restrict = eqsel, join = eqjoinsel );
DROP OPERATOR <== (varchar,varchar);
CREATE OPERATOR <==
( leftarg = varchar,
rightarg = varchar,
procedure = begins_with , restrict = eqsel, join = eqjoinsel );
3. Inserted record for begins_with in pg_ampop:
INSERT INTO pg_amop
(amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT
(SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_ops'),
6,
false,
(SELECT o.oid FROM pg_operator o, pg_type t WHERE o.oprname = '<==' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar');
INSERT INTO pg_amop
(amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT
(SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_ops'),
6,
false,
(SELECT o.oid FROM pg_operator o, pg_type t WHERE o.oprname = '<==' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar');
4. Created index on table:
CREATE INDEX ix_zemlje_naziv ON zemlje USING btree(naziv);
5. When I try to get plan for this :
EXPLAIN SELECT * from zemlje WHERE naziv<=='A';
I got :
Index Scan using ix_zemlje_naziv on zemlje (cost=0.00..17.07 rows=5 width=405)
It seem that everything is ok, but when I execute the query I got empty recordset.
Function begins_with returns true, I did check it, but recordset is empty.
CREATE INDEX ix_zemlje_naziv ON zemlje USING btree(naziv);
5. When I try to get plan for this :
EXPLAIN SELECT * from zemlje WHERE naziv<=='A';
I got :
Index Scan using ix_zemlje_naziv on zemlje (cost=0.00..17.07 rows=5 width=405)
It seem that everything is ok, but when I execute the query I got empty recordset.
Function begins_with returns true, I did check it, but recordset is empty.
When I skip step 3, results are fine, but operator is not using index !
Is there way to do this ?
Best regards !
"Darko Prenosil" <Darko.Prenosil@finteh.hr> writes: > I tried to write a "begins with" operator to use it instead of LIKE 'xx%', > because LIKE 'xx%' can't use index. Why not? It should be able to --- unless you're using a non-C locale, in which case the problem is fundamental and not easily worked around just by creating another operator. > 3. Inserted record for begins_with in pg_ampop: You can't just invent any old operator and then plop it into pg_amop with a randomly-chosen strategy number. Btree indexes only know about strategy numbers 1 to 5, and those numbers have very definite implications about the semantics of the operator: the operator had better behave as <, <=, =, >=, or > (not sure which one is which number) with respect to the standard sort ordering of the indexed datatype. The other index types also have preconceived notions about the meaning of the strategy numbers that they understand. > 5. When I try to get plan for this : > EXPLAIN SELECT * from zemlje WHERE naziv<=3D=3D'A';=20 > =20 > I got : > Index Scan using ix_zemlje_naziv on zemlje (cost=3D0.00..17.07 rows=3D5 w= > idth=3D405) > =20 > It seem that everything is ok, but when I execute the query I got empty rec= > ordset. I'm surprised you didn't get an Assert failure. The nbtree routines have no idea what to do with strategy number 6. regards, tom lane
----- Original Message ----- From: "Tom Lane" <tgl@sss.pgh.pa.us> To: "Darko Prenosil" <Darko.Prenosil@finteh.hr> Cc: "pgsql interfaces" <pgsql-interfaces@postgresql.org> Sent: Tuesday, December 04, 2001 4:11 PM Subject: Re: [INTERFACES] Operator class and index > "Darko Prenosil" <Darko.Prenosil@finteh.hr> writes: > > I tried to write a "begins with" operator to use it instead of LIKE 'xx%', > > because LIKE 'xx%' can't use index. > > Why not? It should be able to --- unless you're using a non-C locale, > in which case the problem is fundamental and not easily worked around > just by creating another operator. > Yes, I'm using latin2 !!! I did not try to write another LIKE operator, that can match pattern anywhere in the search word, but only at the begining of the word. Finaly I succed to write such operator as You can see below. > > 3. Inserted record for begins_with in pg_ampop: > > You can't just invent any old operator and then plop it into pg_amop > with a randomly-chosen strategy number. Btree indexes only know about > strategy numbers 1 to 5, and those numbers have very definite > implications about the semantics of the operator: the operator had > better behave as <, <=, =, >=, or > (not sure which one is which number) > with respect to the standard sort ordering of the indexed datatype. > The other index types also have preconceived notions about the meaning > of the strategy numbers that they understand. > O.K. I was foolish and did not read carefuly the documentation, I know now that for btree index strategies are: 1 < 2 <= 3 = 4 >= 5 > When You explain to me that I can't make operators with strategy numbers at my own will, I did create new operator class called "varchar_begins_ops": CREATE OR REPLACE FUNCTION varchar_begins_with(varchar, varchar) RETURNS boolean AS ' --Operator greater or equal DECLARE strInput ALIAS FOR $1; strMatch ALIAS FOR $2; nLenMatchINTEGER; nLenStr INTEGER; BEGIN nLenStr=char_length(strInput); nLenMatch=char_length(strMatch); IF nLenStr<nLenMatch THEN nLenMatch=nLenStr; END IF; IF substr(strInput,1,nLenMatch)=strMatch THEN RETURN true; END IF; RETURN false; END; ' LANGUAGE 'plpgsql'; CREATE OPERATOR |<( leftarg = varchar,rightarg = varchar,procedure = varcharlt , restrict = eqsel, join = eqjoinsel ); CREATE OPERATOR |<=( leftarg = varchar,rightarg = varchar,procedure = varcharle , restrict = eqsel, join = eqjoinsel ); CREATE OPERATOR |=( leftarg = varchar,rightarg = varchar,procedure = varchareq , restrict = eqsel, join = eqjoinsel ); CREATE OPERATOR |>=( leftarg = varchar,rightarg = varchar,procedure = varchar_begins_with , restrict = eqsel, join = eqjoinsel); CREATE OPERATOR |>( leftarg = varchar,rightarg = varchar,procedure = varchargt , restrict = eqsel, join = eqjoinsel ); INSERT INTO pg_opclass (opcamid, opcname, opcintype, opcdefault, opckeytype) VALUES ( (SELECT oid FROM pg_am WHEREamname = 'btree'), 'varchar_begins_ops', (SELECT oid FROM pg_type WHERE typname = 'varchar'), true, 0); INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT (SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'), 1, false, (SELECT o.oid FROM pg_operatoro, pg_type t WHERE o.oprname = '|<' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar'); INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT (SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'), 2, false, (SELECT o.oid FROM pg_operatoro, pg_type t WHERE o.oprname = '|<=' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar'); INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT (SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'), 3, false, (SELECT o.oid FROM pg_operatoro, pg_type t WHERE o.oprname = '|=' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar'); INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT (SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'), 4, false, (SELECT o.oid FROM pg_operatoro, pg_type t WHERE o.oprname = '|>=' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar'); INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) SELECT (SELECT pg_opclass.oid FROM pg_opclass WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'), 5, false, (SELECT o.oid FROM pg_operatoro, pg_type t WHERE o.oprname = '|>' and o.oprleft = t.oid and o.oprright = t.oid and t.typname = 'varchar'); INSERT INTO pg_amproc (amopclaid, amprocnum, amproc) SELECT opcl.oid, 1, p.oid FROM pg_opclass opcl, pg_procp WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND opcname = 'varchar_begins_ops'AND p.proname = 'varcharcmp'; As You can see I replaced only function for operator >=, and other operators are set to equivalent functions as for "varchar_ops" operator class. I indexed table zemlje as follows: CREATE index ix_b_zemlje on zemlje(naziv varchar_begins_ops); here is execution plan for : explain select * from zemlje where naziv |>= 'A' Index Scan using ix_b_zemlje on zemlje (cost=0.00..6.17 rows=1 width=405) It is working just fine !!! Result sets are also O.K. Thanks !!! Darko
Hello all. Please, help me, How can I get single field (attribute) value just inserted new row into the table (by not using select query to find this row)? (type of that field is serial), --- lexx