diff --git a/cmake/lantern.control.template b/cmake/lantern.control.template index 7d57497b8..f98813c29 100644 --- a/cmake/lantern.control.template +++ b/cmake/lantern.control.template @@ -1,4 +1,4 @@ -comment = 'LanternDB: Fast vector embedding processing in Postgres' +comment = 'Lantern: Fast vector embedding processing in Postgres' default_version = '@LANTERNDB_VERSION@' module_pathname = '$libdir/lantern' -relocatable = true \ No newline at end of file +relocatable = false diff --git a/sql/lantern.sql b/sql/lantern.sql index ab1fe7d25..b2c9c5b21 100644 --- a/sql/lantern.sql +++ b/sql/lantern.sql @@ -29,8 +29,9 @@ CREATE OPERATOR <-> ( COMMUTATOR = '<->' ); +CREATE SCHEMA _lantern_internal; -- operator classes -CREATE OR REPLACE FUNCTION _create_ldb_operator_classes(access_method_name TEXT) RETURNS BOOLEAN AS $$ +CREATE OR REPLACE FUNCTION _lantern_internal._create_ldb_operator_classes(access_method_name TEXT) RETURNS BOOLEAN AS $$ DECLARE dist_l2sq_ops TEXT; dist_cos_ops TEXT; @@ -107,13 +108,13 @@ BEGIN IF hnsw_am_exists THEN - PERFORM _create_ldb_operator_classes('lantern_hnsw'); + PERFORM _lantern_internal._create_ldb_operator_classes('lantern_hnsw'); RAISE WARNING 'Access method(index type) "hnsw" already exists. Creating lantern_hnsw access method'; ELSE -- create access method CREATE ACCESS METHOD hnsw TYPE INDEX HANDLER hnsw_handler; COMMENT ON ACCESS METHOD hnsw IS 'LanternDB access method for vector embeddings, based on the hnsw algorithm'; - PERFORM _create_ldb_operator_classes('hnsw'); + PERFORM _lantern_internal._create_ldb_operator_classes('hnsw'); END IF; END; $BODY$ diff --git a/test/expected/ext_relocation.out b/test/expected/ext_relocation.out new file mode 100644 index 000000000..c5e0db07c --- /dev/null +++ b/test/expected/ext_relocation.out @@ -0,0 +1,112 @@ +\ir utils/small_world_array.sql +CREATE TABLE small_world ( + id VARCHAR(3), + b BOOLEAN, + v REAL[3] +); +INSERT INTO small_world (id, b, v) VALUES + ('000', TRUE, '{0,0,0}'), + ('001', TRUE, '{0,0,1}'), + ('010', FALSE, '{0,1,0}'), + ('011', TRUE, '{0,1,1}'), + ('100', FALSE, '{1,0,0}'), + ('101', FALSE, '{1,0,1}'), + ('110', FALSE, '{1,1,0}'), + ('111', TRUE, '{1,1,1}'); +DROP EXTENSION lantern; +\set ON_ERROR_STOP off +-- make sure the extension was dropped. +CREATE INDEX ON small_world USING hnsw (v) WITH (dim=3); +ERROR: access method "hnsw" does not exist +\set ON_ERROR_STOP on +-- test creating lantern on different schemas +CREATE SCHEMA schema1; +CREATE SCHEMA schema2; +CREATE EXTENSION lantern WITH SCHEMA schema1; +-- show all the extension functions and operators +SELECT ne.nspname AS extschema, p.proname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = p.pronamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + extschema | proname | proschema +-----------+------------------------------+------------------- + schema1 | _create_ldb_operator_classes | _lantern_internal + schema1 | ldb_generic_dist | schema1 + schema1 | ldb_generic_dist | schema1 + schema1 | hnsw_handler | schema1 + schema1 | cos_dist | schema1 + schema1 | hamming_dist | schema1 + schema1 | l2sq_dist | schema1 +(7 rows) + +-- show all the extension operators +SELECT ne.nspname AS extschema, op.oprname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_operator AS op ON (op.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = op.oprnamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + extschema | oprname | proschema +-----------+---------+----------- + schema1 | <-> | schema1 + schema1 | <-> | schema1 +(2 rows) + +SET search_path TO public, schema1; +-- extension function is accessible +SELECT l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]); + l2sq_dist +----------- + 27 +(1 row) + +CREATE INDEX hnsw_index ON small_world USING hnsw(v) WITH (dim=3); +INFO: done init usearch index +INFO: inserted 8 elements +INFO: done saving 8 vectors +\set ON_ERROR_STOP off +-- lantern does not support relocation. +-- Postgres will not allow it to support this since its objects span over more than one schema +ALTER EXTENSION lantern SET SCHEMA schema2; +ERROR: extension "lantern" does not support SET SCHEMA +-- this will fail because functions from extension lantern in schema1 are in search path and will conflict +CREATE EXTENSION lantern WITH SCHEMA schema2; +ERROR: extension "lantern" already exists +\set ON_ERROR_STOP on +SELECT ne.nspname AS extschema, op.oprname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_operator AS op ON (op.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = op.oprnamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + extschema | oprname | proschema +-----------+---------+----------- + schema1 | <-> | schema1 + schema1 | <-> | schema1 +(2 rows) + +SET search_path TO public, schema2; +--extension access method is still accessible since access methods are not schema-qualified +CREATE INDEX hnsw_index2 ON small_world USING hnsw(v) WITH (dim=3); +INFO: done init usearch index +INFO: inserted 8 elements +INFO: done saving 8 vectors +\set ON_ERROR_STOP off +-- extension function cannot be found without schema-qualification +SELECT l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]); +ERROR: function l2sq_dist(numeric[], numeric[]) does not exist at character 8 +\set ON_ERROR_STOP on +SELECT schema1.l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]); + l2sq_dist +----------- + 27 +(1 row) + diff --git a/test/schedule.txt b/test/schedule.txt index 5969a626a..6444e1efd 100644 --- a/test/schedule.txt +++ b/test/schedule.txt @@ -4,4 +4,4 @@ # - 'test' lines may have multiple space-separated tests. All tests in a single 'test' line will be run in parallel test_pgvector: hnsw_vector -test: hnsw_config hnsw_correct hnsw_create hnsw_create_expr hnsw_dist_func hnsw_insert hnsw_select hnsw_todo hnsw_index_from_file hnsw_cost_estimate +test: hnsw_config hnsw_correct hnsw_create hnsw_create_expr hnsw_dist_func hnsw_insert hnsw_select hnsw_todo hnsw_index_from_file hnsw_cost_estimate ext_relocation diff --git a/test/sql/ext_relocation.sql b/test/sql/ext_relocation.sql new file mode 100644 index 000000000..1748da2f9 --- /dev/null +++ b/test/sql/ext_relocation.sql @@ -0,0 +1,66 @@ +\ir utils/small_world_array.sql + +DROP EXTENSION lantern; +\set ON_ERROR_STOP off +-- make sure the extension was dropped. +CREATE INDEX ON small_world USING hnsw (v) WITH (dim=3); +\set ON_ERROR_STOP on + +-- test creating lantern on different schemas +CREATE SCHEMA schema1; +CREATE SCHEMA schema2; +CREATE EXTENSION lantern WITH SCHEMA schema1; + +-- show all the extension functions and operators +SELECT ne.nspname AS extschema, p.proname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = p.pronamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + +-- show all the extension operators +SELECT ne.nspname AS extschema, op.oprname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_operator AS op ON (op.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = op.oprnamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + +SET search_path TO public, schema1; + +-- extension function is accessible +SELECT l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]); + +CREATE INDEX hnsw_index ON small_world USING hnsw(v) WITH (dim=3); + +\set ON_ERROR_STOP off +-- lantern does not support relocation. +-- Postgres will not allow it to support this since its objects span over more than one schema +ALTER EXTENSION lantern SET SCHEMA schema2; +-- this will fail because functions from extension lantern in schema1 are in search path and will conflict +CREATE EXTENSION lantern WITH SCHEMA schema2; +\set ON_ERROR_STOP on + +SELECT ne.nspname AS extschema, op.oprname, np.nspname AS proschema +FROM pg_catalog.pg_extension AS e + INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid) + INNER JOIN pg_catalog.pg_operator AS op ON (op.oid = d.objid) + INNER JOIN pg_catalog.pg_namespace AS ne ON (ne.oid = e.extnamespace) + INNER JOIN pg_catalog.pg_namespace AS np ON (np.oid = op.oprnamespace) +WHERE d.deptype = 'e' AND e.extname = 'lantern' +ORDER BY 1, 3; + +SET search_path TO public, schema2; +--extension access method is still accessible since access methods are not schema-qualified +CREATE INDEX hnsw_index2 ON small_world USING hnsw(v) WITH (dim=3); + +\set ON_ERROR_STOP off +-- extension function cannot be found without schema-qualification +SELECT l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]); +\set ON_ERROR_STOP on +SELECT schema1.l2sq_dist(ARRAY[1.0, 2.0, 3.0], ARRAY[4.0, 5.0, 6.0]);