Compare commits

...

65 Commits

Author SHA1 Message Date
Raul Ochoa
41a2c7363e Merge tag '0.3.6' into cdb
Release 0.3.6
2014-08-11 16:39:20 +02:00
Raul Ochoa
45d07bc5a8 Release 0.3.6 2014-08-11 16:38:41 +02:00
Raul Ochoa
55615e1a32 Stubs next version 2014-08-11 16:18:02 +02:00
Raul Ochoa
4b397d6bc0 Merge tag '0.3.5'
Release 0.3.5
2014-08-11 16:16:50 +02:00
Raul Ochoa
8349e3c94c Merge tag '0.3.5' into cdb
Release 0.3.5 fixed
2014-08-11 16:14:00 +02:00
Raul Ochoa
a747f16ecc Fixes release dates 2014-08-11 16:13:04 +02:00
Raul Ochoa
93de996acc Fixes 0.3.5 version:
- Version in Makefile
- Upgrade path from 0.3.4
2014-08-11 15:41:53 +02:00
Raul Ochoa
a98b100182 Ignore idea ide based settings 2014-08-11 15:41:33 +02:00
Kartones
72d8cf6210 Merge pull request #46 from CartoDB/CDB-3870
CDB-3870
2014-08-11 15:30:11 +02:00
Kartones
6c57640901 CDB-3870 replaced priority of qmax inside checkquota 2014-08-11 15:26:13 +02:00
Raul Ochoa
a67f324001 Release 0.3.4 version 2014-08-01 18:21:48 +02:00
Raul Ochoa
5bb638b995 Merge pull request #45 from CartoDB/CDB-3743
CDB_QueryTables returns only results for schemas associated to Relation-Names
2014-08-01 18:14:53 +02:00
Raul Ochoa
2637742c2e CDB-3743 Makes CDB_QueryTables to return only results for schemas associated to Relation-Names 2014-08-01 18:06:06 +02:00
Kartones
b75b492490 Merge pull request #44 from CartoDB/CDB-3497
Cdb 3497
2014-07-30 16:01:09 +02:00
Kartones
f9ec3c46ee Merge branch 'master' into CDB-3497
Conflicts:
	Makefile
	NEWS
2014-07-29 14:16:52 +02:00
Kartones
679af1d4a3 CDB-3497 Finished stabilizing specs 2014-07-28 17:53:19 +02:00
Raul Ochoa
a9005dc93c Updates apt-get so it can pick up latest postgres packages 2014-07-28 15:49:43 +02:00
Raul Ochoa
e81a75b716 Make 0.3.0dev version upgradeable 2014-07-28 15:39:19 +02:00
Kartones
6d665ab163 CDB-3497 ongoing spec stabilization 2014-07-28 15:16:19 +02:00
Kartones
e5c8015e17 CDB-3497 Splitted 2014-07-28 13:11:51 +02:00
Kartones
f61d07518d CDB-3497 ongoing splitting 2014-07-28 12:25:42 +02:00
Kartones
b5589fdf09 CDB-3497 2014-07-25 12:19:40 +02:00
Kartones
de2d3f818e CDB-3497 Updating travis.yml to make it work with newer PG version 2014-07-25 10:56:54 +02:00
Kartones
9500010a67 CDB-3497 Partial splitting 2014-07-25 10:52:46 +02:00
Luis Bosque
eff2f8b619 Dummy version. Start using semanting versioning 2014-07-22 18:15:17 +02:00
Raul Ochoa
f1504fb0b1 Make dev flavour upgradeable to current version 2014-07-17 15:43:19 +02:00
Raul Ochoa
13140dbc63 Version 0.3.0 2014-07-16 12:30:19 +02:00
Luis Bosque
348b8871f3 Update NEWS 2014-07-15 16:57:23 +02:00
Raul Ochoa
2bd0b223a6 CDB_SetUserQuotaInBytes returning set value instead of previous value. 2014-07-07 10:36:51 +02:00
Raul Ochoa
5806ac8f8d Force schema so it can calculate the size of the database with the proper schema 2014-07-04 18:50:46 +02:00
Kartones
1c86599f9d CDB-3094 Discovered that EXECUTE FORMAT seems to quote schema names when it needs to 2014-07-04 16:39:19 +02:00
Kartones
3c5251e6c6 CDB-3303 Cartodbfication also supporting optional schemas 2014-07-04 15:55:08 +02:00
Kartones
3d595976f6 CDB-3302 CDB_CheckQuota now also accepts a schema name. Added a convenience method to check if a schema is valid or not 2014-07-04 11:48:53 +02:00
Kartones
021d922d84 CDB-3304 Method cdb_userdatasize now has schema_name param, and added backwards-compatible version without it 2014-07-04 10:57:55 +02:00
Luis Bosque
82f2e7336b Quoted schema references in organization sql 2014-07-03 15:33:36 +02:00
Raul Ochoa
1573a199c8 Merge remote-tracking branch 'origin/delete_permission' into CDB-3094 2014-06-27 01:37:53 +01:00
Raul Ochoa
558be8b693 CDB-3094 DELETE permission for read-write rule 2014-06-27 00:01:25 +01:00
javi
77182321ff added delete permission with write permission for organization 2014-06-26 15:50:56 +02:00
Raul Ochoa
b98419aeb8 Merge remote-tracking branch 'rambo/cdb_querytables_support_schemas' into CDB-3094 2014-06-26 08:12:41 +02:00
javi
da9b9a640f CDB_QueryTables return tables with schema 2014-06-25 19:38:29 +02:00
Raul Ochoa
84174c4f71 CDB-3094 no hardcoded coloring output by creating a new log_warning function 2014-06-25 12:43:46 +02:00
Raul Ochoa
55efde5039 CDB-3094 Makes organization member group role name unique to database by using the database name md5 hash 2014-06-25 12:38:14 +02:00
Raul Ochoa
8624dd1a00 CDB-3094 Improves bash test runner by displaying failing tests after execution 2014-06-25 12:37:09 +02:00
Raul Ochoa
12e90ef6a4 CDB-3094 Adds cdb_org_member role group and functions to handle access to tables through it 2014-06-24 19:56:17 +02:00
Raul Ochoa
625b01ed56 CDB-3094 Removes for now the role groups management as it is not clear if we need them 2014-06-19 20:16:10 +02:00
Raul Ochoa
5e5471c474 CDB-3094 changes signature to allow specifying the schema because it does not have to be the role name.
- fixes tests to match new signature.
- does not revoke access to the schema when revoking access to a table. TODO
2014-06-19 20:16:10 +02:00
Sandro Santilli
d61de906d5 Use "cdb_test" prefix for test member names
User names are global to a cluster, better try to reduce likelyhood
of clashes
2014-06-18 11:57:12 +02:00
Sandro Santilli
91bec69a68 Have travis specify "postgres" as the testsuite running user 2014-06-18 11:54:28 +02:00
Sandro Santilli
50060a2529 Add note about the need to be postgresql superuser when running tests 2014-06-18 11:52:01 +02:00
Sandro Santilli
36dca36927 Do not assume "postgres" user exists 2014-06-18 11:49:56 +02:00
Raul Ochoa
2743b1767b Merge branch 'master' of https://github.com/CartoDB/cartodb-postgresql into CDB-3094 2014-06-17 11:44:34 +02:00
Raul Ochoa
b69b2fbdcf CDB-3094 enables organizations functions 2014-06-17 11:43:38 +02:00
Sandro Santilli
1815096a37 Move in-place upgrade of functions from README to CONTRIBUTING 2014-06-17 10:43:49 +02:00
Sandro Santilli
44d32e4db5 Add CONTRIBUTING file 2014-06-17 10:38:18 +02:00
Raul Ochoa
8dec91de87 Merge branch 'master' of https://github.com/CartoDB/cartodb-postgresql into CDB-3094 2014-06-16 18:56:34 +02:00
Raul Ochoa
fb68f225d5 CDB-3094 no hardcoded database. it also creates the database between tests and the cartodb schema to install the functions 2014-06-16 17:13:13 +02:00
Raul Ochoa
9d7de169fb CDB-3094 use bash command instead of sh 2014-06-16 16:28:28 +02:00
Raul Ochoa
9a55b03d6f CDB-3094 initial function to share tables/schemas between users within the same database 2014-06-16 16:10:53 +02:00
Sandro Santilli
1bb8b85503 Merge branch 'b0.2'
Conflicts:
	Makefile
	NEWS
2014-06-11 19:30:32 +02:00
Sandro Santilli
9afc59da5e Set release date for 0.2.1 2014-06-11 19:28:24 +02:00
Sandro Santilli
bc0966c46e Do not add unique index on cartodb_id if already a primary key
Closes #38
2014-06-11 19:21:19 +02:00
Sandro Santilli
afcc2498c8 List plpythonu requirement first, so get pg_catalog scanned before public
Since "plpythonu" is installed in "pg_catalog" schema, requiring it
from cartodb.control adds the "pg_catalog" to the search_path.
Schemas are added to search_path in order of appearence in the "requires"
directive of the cartodb.contorl, right after the name of schema
written in the "schema" directive.

So this commit changes the resulting search_path from:

 cartodb,schema_triggers,public,pg_catalog

To:

 cartodb,pg_catalog,schema_triggers,public

Preventing presence of objects in the "public" schema from changing
interpretation of function signatures and body from this extension.

Spotted in the real world in presence of a "date" table changing
intepretation of CDB_StringToDate function.
2014-06-11 16:27:11 +02:00
Sandro Santilli
81bbfcbda8 Drop useless DEFAULT specification in plpgsql variable declarations 2014-06-11 11:25:25 +02:00
Sandro Santilli
9dbe854df7 Do not force re-cartodbfication on CREATE FROM unpackaged 2014-06-11 10:24:53 +02:00
Sandro Santilli
5a8b9021c8 Prepare for 0.3.0 2014-06-09 13:04:42 +02:00
23 changed files with 1012 additions and 287 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ results/
regression.*
expected/test
sql/test
.idea/*

View File

@@ -4,7 +4,9 @@ addons:
postgresql: 9.3
before_install:
- sudo apt-get update
#- sudo apt-get install -q postgresql-9.3-postgis-2.1
- sudo apt-get update
- sudo apt-get install -q postgresql-server-dev-9.3
- sudo apt-get install -q postgresql-plpython-9.3
# Install schema_triggers
@@ -19,5 +21,5 @@ before_install:
script:
- make
- sudo make install
- PGOPTIONS='-c client_min_messages=NOTICE' make installcheck ||
- PGOPTIONS='-c client_min_messages=NOTICE' PGUSER=postgres make installcheck ||
{ cat regression.diffs; false; }

61
CONTRIBUTING Normal file
View File

@@ -0,0 +1,61 @@
The development tracker for cartodb-postgresql is on github:
http://github.com/cartodb/cartodb-postgresql/
Bug fixes are best reported as pull requests over there.
Features are best discussed on the mailing list:
https://groups.google.com/d/forum/cartodb
Adding features to the extension
--------------------------------
Extension features are coded in scripts found under the
"scripts-available" directory. A feature can be a single function
or a group of function with a specific scope.
The "scripts-enabled" directory contains symlinks to the scripts
in "scripts-available". Any symlink in that directory is automatically
included in the extension. Numbering can be used to enforce the order
in which those scripts are loaded.
Scripts would be best coded in a way to be usable both for creation
and upgrade of the objects. This means using CREATE OR REPLACE for
the functions, and whatever it takes to check existance of any previous
version of objects in other cases.
When used as an extension (probably always from version 0.2.0 onwards)
all the objects will be installed in a "cartodb" schema. Take this into
account to fully-qualify internal calls to avoid (possibly dangerous)
name clashes.
Every new feature (as well as bugfixes) should come with a testcase,
see next session.
Writing testcases
-----------------
Tests reside in the test/ directory.
You can find information about how to write tests in test/README
Testing changes live
--------------------
Testing changes made during development requires upgrading
the extension into your test database.
During development the cartodb extension version doesn't change with
every commit, so testing latest change requires cheating with PostgreSQL
so to enforce re-load of the scripts. To help with cheating, "make install"
also installs migration scripts to go from "V" to "V"next and from "V"next
to "V". Example to upgrade a 0.2.0dev version:
```sql
ALTER EXTENSION cartodb UPDATE TO '0.2.0devnext';
ALTER EXTENSION cartodb UPDATE TO '0.2.0dev';
```
Starting with 0.2.0, the in-place reload can be done with an ad-hoc function:
```sql
SELECT cartodb.cdb_extension_reload();
```

View File

@@ -1,7 +1,7 @@
# cartodb/Makefile
EXTENSION = cartodb
EXTVERSION = 0.2.0
EXTVERSION = 0.3.6
SED = sed
@@ -17,7 +17,16 @@ UPGRADABLE = \
unpackaged \
0.1.0 \
0.1.1 \
0.2.0dev \
0.2.0 \
0.2.1 \
0.3.0 \
0.3.0dev \
0.3.1 \
0.3.2 \
0.3.3 \
0.3.4 \
0.3.5 \
$(EXTVERSION)dev \
$(EXTVERSION)next \
$(END)
@@ -89,7 +98,10 @@ legacy_regress: $(REGRESS_OLD) Makefile
cat test/$${tn}_expect >> $${exp}; \
done
test_organization:
bash test/organization/test.sh
legacy_tests: legacy_regress
installcheck: legacy_tests
installcheck: legacy_tests test_organization

42
NEWS
View File

@@ -1,3 +1,45 @@
0.3.6 (2014-08-11)
------------------
Dummy release to solve some issues with cdb branch/tag
0.3.5 (2014-08-11)
------------------
Inverting priority of CDB_CheckQuota qmax so gies more priority to existing user quota function over parameter value.
0.3.4 (2014-08-01)
------------------
Fixes issue with schemas in CDB_QueryTables
0.3.3 (2014-07-30)
------------------
* Splitting of CartodbfyTable method in subfunctions to be able to call in fragments and evade timeouts on hot zones
0.3.2 (2014-07-28)
------------------
* Make 0.3.0dev version upgradeable
0.3.1 (2014-07-22)
------------------
* Dummy version. We start using semantic versioning
0.3.0 (2014-07-15)
------------------
* Permission management functions
* Adapt functions to use schemas
0.2.1 - 2014-06-11
------------------
Enhancements:
- Do not force re-cartodbfication on CREATE FROM unpackaged
- Drop useless DEFAULT specification in plpgsql variable declarations
- List plpythonu requirement first, to get pg_catalog scanned before public
Bug fixes:
- Do not add unique index on cartodb_id if already a primary key (#38)
0.2.0 - 2014-06-09
------------------

View File

@@ -31,6 +31,9 @@ NOTE: if ``test_ddl_triggers`` fails it's likely due to an incomplete
installation of schema_triggers: you need to add ``schema_triggers.so``
to the ``shared_preload_libraries`` setting in postgresql.conf !
NOTE: you need to run the installcheck as a superuser, use PGUSER
env variable if needed, like: PGUSER=postgres make installcheck
Enable database
---------------
@@ -87,19 +90,5 @@ ls `pg_config --sharedir`/extension/cartodb*
```
During development the cartodb extension version doesn't change with
every commit, so testing latest change requires cheating with PostgreSQL
so to enforce re-load of the scripts. To help with cheating, "make install"
also installs migration scripts to go from "V" to "V"next and from "V"next
to "V". Example to upgrade a 0.2.0dev version:
```sql
ALTER EXTENSION cartodb UPDATE TO '0.2.0devnext';
ALTER EXTENSION cartodb UPDATE TO '0.2.0dev';
```
Starting with 0.2.0, the in-place reload can be done with an ad-hoc function:
```sql
SELECT cartodb.cdb_extension_reload();
```
every commit, so testing latest change requires special steps documented
in the CONTRIBUTING document, under "Testing changes live".

View File

@@ -3,4 +3,4 @@ comment = 'Turn a database into a cartodb user database.'
superuser = true
relocatable = false
schema = cartodb
requires = 'schema_triggers, postgis, plpythonu'
requires = 'plpythonu, schema_triggers, postgis'

View File

@@ -6,49 +6,35 @@
-- (user.rebuild_quota_trigger, called by rake task
-- cartodb:db:update_test_quota_trigger)
-- Update the_geom_webmercator
CREATE OR REPLACE FUNCTION _CDB_update_the_geom_webmercator()
RETURNS trigger AS $$
BEGIN
NEW.the_geom_webmercator := public.CDB_TransformToWebmercator(NEW.the_geom);
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION _CDB_update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;
-- Ensure a table is a "cartodb" table
-- See https://github.com/CartoDB/cartodb/wiki/CartoDB-user-table
CREATE OR REPLACE FUNCTION CDB_CartodbfyTable(reloid REGCLASS)
RETURNS void
-- 1) Required checks before running cartodbfication
-- Either will pass silenty or raise an exception
CREATE OR REPLACE FUNCTION _CDB_check_prerequisites(schema_name TEXT, reloid REGCLASS)
RETURNS void
AS $$
DECLARE
sql TEXT;
rec RECORD;
rec2 RECORD;
tabinfo RECORD;
had_column BOOLEAN;
i INTEGER;
new_name TEXT;
quota_in_bytes INT8;
exists_geom_cols BOOLEAN[];
BEGIN
IF cartodb.schema_exists(schema_name) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
END IF;
-- TODO: Check that user quota is set ?
BEGIN
PERFORM public._CDB_UserQuotaInBytes();
EXCEPTION WHEN undefined_function THEN
EXECUTE FORMAT('SELECT %I._CDB_UserQuotaInBytes();', schema_name::text) INTO sql;
EXCEPTION WHEN undefined_function THEN
RAISE EXCEPTION 'Please set user quota before cartodbfying tables.';
END;
END;
$$ LANGUAGE PLPGSQL;
-- Drop cartodb triggers (might prevent changing columns)
-- 2) Drop cartodb triggers (might prevent changing columns)
CREATE OR REPLACE FUNCTION _CDB_drop_triggers(reloid REGCLASS)
RETURNS void
AS $$
DECLARE
sql TEXT;
BEGIN
-- "track_updates"
sql := 'DROP TRIGGER IF EXISTS track_updates ON ' || reloid::text;
EXECUTE sql;
@@ -66,10 +52,22 @@ BEGIN
EXECUTE sql;
sql := 'DROP TRIGGER IF EXISTS test_quota_per_row ON ' || reloid::text;
EXECUTE sql;
END;
$$ LANGUAGE PLPGSQL;
-- Ensure required fields exist
-- We need a cartodb_id column
-- 3) Cartodb_id creation & validation or renaming if invalid
CREATE OR REPLACE FUNCTION _CDB_create_cartodb_id_column(reloid REGCLASS)
RETURNS void
AS $$
DECLARE
sql TEXT;
rec RECORD;
rec2 RECORD;
had_column BOOLEAN;
i INTEGER;
new_name TEXT;
BEGIN
<< cartodb_id_setup >>
LOOP --{
had_column := FALSE;
@@ -78,17 +76,15 @@ BEGIN
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXIT cartodb_id_setup;
EXCEPTION
WHEN duplicate_column THEN
RAISE NOTICE 'Column cartodb_id already exists';
had_column := TRUE;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (cartodb_id): % (%)',
reloid, SQLERRM, SQLSTATE;
EXCEPTION
WHEN duplicate_column THEN
RAISE NOTICE 'Column cartodb_id already exists';
had_column := TRUE;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (cartodb_id): % (%)', reloid, SQLERRM, SQLSTATE;
END;
IF had_column THEN
SELECT pg_catalog.pg_get_serial_sequence(reloid::text, 'cartodb_id')
AS seq INTO rec2;
@@ -96,9 +92,9 @@ BEGIN
SELECT
pg_catalog.pg_get_serial_sequence(reloid::text, 'cartodb_id') as seq,
t.typname, t.oid, a.attnotnull FROM pg_type t, pg_attribute a
WHERE a.atttypid = t.oid AND a.attrelid = reloid AND NOT a.attisdropped
AND a.attname = 'cartodb_id'
WHERE a.atttypid = t.oid AND a.attrelid = reloid AND NOT a.attisdropped AND a.attname = 'cartodb_id'
INTO STRICT rec;
-- 20=int2, 21=int4, 23=int8
IF rec.oid NOT IN (20,21,23) THEN -- {
RAISE NOTICE 'Existing cartodb_id field is of invalid type % (need int2, int4 or int8), renaming', rec.typname;
@@ -107,11 +103,11 @@ BEGIN
ELSE -- }{
sql := 'ALTER TABLE ' || reloid::text || ' ALTER COLUMN cartodb_id SET NOT NULL';
IF NOT EXISTS ( SELECT c.conname FROM pg_constraint c, pg_attribute a
WHERE c.conkey = ARRAY[a.attnum] AND c.conrelid = reloid
AND a.attrelid = reloid
AND NOT a.attisdropped
AND a.attname = 'cartodb_id'
AND c.contype = 'u' ) -- unique
WHERE c.conkey = ARRAY[a.attnum] AND c.conrelid = reloid
AND a.attrelid = reloid
AND NOT a.attisdropped
AND a.attname = 'cartodb_id'
AND c.contype IN ( 'u', 'p' ) ) -- unique or pkey
THEN
sql := sql || ', ADD unique(cartodb_id)';
END IF;
@@ -119,16 +115,14 @@ BEGIN
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXIT cartodb_id_setup;
EXCEPTION
WHEN unique_violation OR not_null_violation THEN
RAISE NOTICE '%, renaming', SQLERRM;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (cartodb_id): % (%)',
reloid, SQLERRM, SQLSTATE;
EXCEPTION
WHEN unique_violation OR not_null_violation THEN
RAISE NOTICE '%, renaming', SQLERRM;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (cartodb_id): % (%)', reloid, SQLERRM, SQLSTATE;
END;
END IF; -- }
-- invalid column, need rename and re-create it
i := 0;
<< rename_column >>
@@ -138,32 +132,29 @@ BEGIN
sql := 'ALTER TABLE ' || reloid::text || ' RENAME COLUMN cartodb_id TO ' || new_name;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (renaming cartodb_id): % (%)',
reloid, SQLERRM, SQLSTATE;
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (renaming cartodb_id): % (%)', reloid, SQLERRM, SQLSTATE;
END;
EXIT rename_column;
END LOOP; --}
CONTINUE cartodb_id_setup;
END IF;
END LOOP; -- }
-- Try to copy data from new name if possible
-- Try to copy data from new name if possible
IF new_name IS NOT NULL THEN
RAISE NOTICE 'Trying to recover data from % column', new_name;
BEGIN
-- Copy existing values to new field
-- NOTE: using ALTER is a workaround to a PostgreSQL bug and
-- is also known to be faster for tables with many rows
-- NOTE: using ALTER is a workaround to a PostgreSQL bug and is also known to be faster for tables with many rows
-- See http://www.postgresql.org/message-id/20140530143150.GA11051@localhost
sql := 'ALTER TABLE ' || reloid::text
|| ' ALTER cartodb_id TYPE int USING '
|| new_name || '::int4';
|| ' ALTER cartodb_id TYPE int USING '
|| new_name || '::int4';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
@@ -178,23 +169,39 @@ BEGIN
-- Reset sequence name
sql := 'ALTER SEQUENCE ' || rec2.seq::text
|| ' RESTART WITH ' || rec.max + 1;
|| ' RESTART WITH ' || rec.max + 1;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
-- Drop old column (all went find if we got here)
-- Drop old column (all went fine if we got here)
sql := 'ALTER TABLE ' || reloid::text || ' DROP ' || new_name;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN others THEN
RAISE NOTICE 'Could not initialize cartodb_id with existing values: % (%)',
EXCEPTION
WHEN others THEN
RAISE NOTICE 'Could not initialize cartodb_id with existing values: % (%)',
SQLERRM, SQLSTATE;
END;
END IF;
-- We need created_at and updated_at
END;
$$ LANGUAGE PLPGSQL;
-- 4) created_at and updated_at creation & validation or renaming if invalid
CREATE OR REPLACE FUNCTION _CDB_create_timestamp_columns(reloid REGCLASS)
RETURNS void
AS $$
DECLARE
sql TEXT;
rec RECORD;
rec2 RECORD;
had_column BOOLEAN;
i INTEGER;
new_name TEXT;
BEGIN
FOR rec IN SELECT * FROM ( VALUES ('created_at'), ('updated_at') ) t(cname)
LOOP --{
new_name := null;
@@ -203,48 +210,43 @@ BEGIN
had_column := FALSE;
BEGIN
sql := 'ALTER TABLE ' || reloid::text || ' ADD ' || rec.cname
|| ' TIMESTAMPTZ NOT NULL DEFAULT now()';
|| ' TIMESTAMPTZ NOT NULL DEFAULT now()';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXIT column_setup;
EXCEPTION
WHEN duplicate_column THEN
RAISE NOTICE 'Column % already exists', rec.cname;
had_column := TRUE;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN duplicate_column THEN
RAISE NOTICE 'Column % already exists', rec.cname;
had_column := TRUE;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)', reloid, rec.cname, SQLERRM, SQLSTATE;
END;
IF had_column THEN
-- Check data type is a TIMESTAMP WITH TIMEZONE
SELECT t.typname, t.oid, a.attnotnull FROM pg_type t, pg_attribute a
WHERE a.atttypid = t.oid AND a.attrelid = reloid
AND NOT a.attisdropped AND a.attname = rec.cname
WHERE a.atttypid = t.oid AND a.attrelid = reloid AND NOT a.attisdropped AND a.attname = rec.cname
INTO STRICT rec2;
IF rec2.oid NOT IN (1184) THEN -- timestamptz {
RAISE NOTICE 'Existing % field is of invalid type % (need timestamptz), renaming', rec.
cname, rec2.typname;
RAISE NOTICE 'Existing % field is of invalid type % (need timestamptz), renaming', rec.cname, rec2.typname;
ELSE -- }{
-- Ensure data type is a TIMESTAMP WITH TIMEZONE
sql := 'ALTER TABLE ' || reloid::text
|| ' ALTER ' || rec.cname
|| ' SET NOT NULL,'
|| ' ALTER ' || rec.cname
|| ' SET DEFAULT now()';
|| ' ALTER ' || rec.cname
|| ' SET NOT NULL,'
|| ' ALTER ' || rec.cname
|| ' SET DEFAULT now()';
BEGIN
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXIT column_setup;
EXCEPTION
WHEN not_null_violation THEN -- failed not-null
RAISE NOTICE '%, renaming', SQLERRM;
WHEN cannot_coerce THEN -- failed cast
RAISE NOTICE '%, renaming', SQLERRM;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN not_null_violation THEN -- failed not-null
RAISE NOTICE '%, renaming', SQLERRM;
WHEN cannot_coerce THEN -- failed cast
RAISE NOTICE '%, renaming', SQLERRM;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)', reloid, rec.cname, SQLERRM, SQLSTATE;
END;
END IF; -- }
@@ -257,18 +259,17 @@ cname, rec2.typname;
sql := 'ALTER TABLE ' || reloid::text || ' RENAME COLUMN ' || rec.cname || ' TO ' || new_name;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (renaming %): % (%)',
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (renaming %): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
END;
EXIT rename_column;
END LOOP; --}
CONTINUE column_setup;
END IF;
END LOOP; -- }
@@ -277,12 +278,11 @@ cname, rec2.typname;
RAISE NOTICE 'Trying to recover data from % coumn', new_name;
BEGIN
-- Copy existing values to new field
-- NOTE: using ALTER is a workaround to a PostgreSQL bug and
-- is also known to be faster for tables with many rows
-- NOTE: using ALTER is a workaround to a PostgreSQL bug and is also known to be faster for tables with many rows
-- See http://www.postgresql.org/message-id/20140530143150.GA11051@localhost
sql := 'ALTER TABLE ' || reloid::text || ' ALTER ' || rec.cname
|| ' TYPE TIMESTAMPTZ USING '
|| new_name || '::timestamptz';
|| ' TYPE TIMESTAMPTZ USING '
|| new_name || '::timestamptz';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
@@ -291,21 +291,36 @@ cname, rec2.typname;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN others THEN
RAISE NOTICE 'Could not initialize % with existing values: % (%)',
rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN others THEN
RAISE NOTICE 'Could not initialize % with existing values: % (%)', rec.cname, SQLERRM, SQLSTATE;
END;
END IF; -- }
END LOOP; -- }
END;
$$ LANGUAGE PLPGSQL;
-- 5) the_geom and the_geom_webmercator creation & validation or renaming if invalid
CREATE OR REPLACE FUNCTION _CDB_create_the_geom_columns(reloid REGCLASS)
RETURNS BOOLEAN[]
AS $$
DECLARE
sql TEXT;
rec RECORD;
rec2 RECORD;
had_column BOOLEAN;
i INTEGER;
new_name TEXT;
exists_geom_cols BOOLEAN[];
BEGIN
-- We need the_geom and the_geom_webmercator
FOR rec IN SELECT * FROM ( VALUES ('the_geom',4326), ('the_geom_webmercator',3857) ) t(cname,csrid) LOOP --{
<< column_setup >> LOOP --{
BEGIN
sql := 'ALTER TABLE ' || reloid::text || ' ADD ' || rec.cname
|| ' GEOMETRY(geometry,' || rec.csrid || ')';
|| ' GEOMETRY(geometry,' || rec.csrid || ')';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
sql := 'CREATE INDEX ON ' || reloid::text || ' USING GIST ( ' || rec.cname || ')';
@@ -313,25 +328,22 @@ cname, rec2.typname;
EXECUTE sql;
exists_geom_cols := array_append(exists_geom_cols, false);
EXIT column_setup;
EXCEPTION
WHEN duplicate_column THEN
exists_geom_cols := array_append(exists_geom_cols, true);
RAISE NOTICE 'Column % already exists', rec.cname;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN duplicate_column THEN
exists_geom_cols := array_append(exists_geom_cols, true);
RAISE NOTICE 'Column % already exists', rec.cname;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (%): % (%)', reloid, rec.cname, SQLERRM, SQLSTATE;
END;
<< column_fixup >>
LOOP --{
-- Check data type is a GEOMETRY
SELECT t.typname, t.oid, a.attnotnull,
postgis_typmod_srid(a.atttypmod) as srid,
postgis_typmod_type(a.atttypmod) as gtype
FROM pg_type t, pg_attribute a
WHERE a.atttypid = t.oid AND a.attrelid = reloid AND NOT a.attisdropped
AND a.attname = rec.cname
postgis_typmod_srid(a.atttypmod) as srid,
postgis_typmod_type(a.atttypmod) as gtype
FROM pg_type t, pg_attribute a
WHERE a.atttypid = t.oid AND a.attrelid = reloid AND NOT a.attisdropped AND a.attname = rec.cname
INTO STRICT rec2;
IF rec2.typname NOT IN ('geometry') THEN -- {
@@ -340,46 +352,44 @@ cname, rec2.typname;
END IF; -- }
IF rec2.srid != rec.csrid THEN -- {
BEGIN
sql := 'ALTER TABLE ' || reloid::text || ' ALTER ' || rec.cname
|| ' TYPE geometry(' || rec2.gtype || ',' || rec.csrid || ') USING ST_Transform('
|| rec.cname || ',' || rec.csrid || ')';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
BEGIN
sql := 'ALTER TABLE ' || reloid::text || ' ALTER ' || rec.cname
|| ' TYPE geometry(' || rec2.gtype || ',' || rec.csrid || ') USING ST_Transform('
|| rec.cname || ',' || rec.csrid || ')';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN others THEN
RAISE NOTICE 'Could not enforce SRID % to column %: %, renaming', rec.csrid, rec.cname, SQLERRM;
EXIT column_fixup; -- cannot fix, will rename
END;
END;
END IF; -- }
-- add gist indices if not there already
IF NOT EXISTS ( SELECT ir.relname
FROM pg_am am, pg_class ir,
pg_class c, pg_index i,
pg_attribute a
pg_class c, pg_index i,
pg_attribute a
WHERE c.oid = reloid AND i.indrelid = c.oid
AND a.attname = rec.cname
AND i.indexrelid = ir.oid AND i.indnatts = 1
AND i.indkey[0] = a.attnum AND a.attrelid = c.oid
AND NOT a.attisdropped AND am.oid = ir.relam
AND am.amname = 'gist' )
AND a.attname = rec.cname
AND i.indexrelid = ir.oid AND i.indnatts = 1
AND i.indkey[0] = a.attnum AND a.attrelid = c.oid
AND NOT a.attisdropped AND am.oid = ir.relam
AND am.amname = 'gist' )
THEN -- {
BEGIN
sql := 'CREATE INDEX ON ' || reloid::text || ' USING GIST ( ' || rec.cname || ')';
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (% index): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (% index): % (%)', reloid, rec.cname, SQLERRM, SQLSTATE;
END;
END IF; -- }
-- if we reached this line, all went good
EXIT column_setup;
END LOOP; -- } column_fixup
END LOOP; -- } column_fixup
-- invalid column, need rename and re-create it
i := 0;
@@ -390,73 +400,155 @@ cname, rec2.typname;
sql := 'ALTER TABLE ' || reloid::text || ' RENAME COLUMN ' || rec.cname || ' TO ' || new_name;
RAISE DEBUG 'Running %', sql;
EXECUTE sql;
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (rename %): % (%)',
reloid, rec.cname, SQLERRM, SQLSTATE;
EXCEPTION
WHEN duplicate_column THEN
i := i+1;
CONTINUE rename_column;
WHEN others THEN
RAISE EXCEPTION 'Cartodbfying % (rename %): % (%)', reloid, rec.cname, SQLERRM, SQLSTATE;
END;
EXIT rename_column;
END LOOP; --}
CONTINUE column_setup;
END LOOP; -- } column_setup
END LOOP; -- } column_setup
END LOOP; -- } on expected geometry columns
-- Initialize the_geom with values from the_geom_webmercator
-- do this only if the_geom_webmercator was found (not created)
-- _and_ the_geom was NOT found.
IF exists_geom_cols[2] AND NOT exists_geom_cols[1] THEN
RETURN exists_geom_cols;
END;
$$ LANGUAGE PLPGSQL;
-- 6) Initialize the_geom with values from the_geom_webmercator
-- do this only if the_geom_webmercator was found (not created) and the_geom was NOT found.
CREATE OR REPLACE FUNCTION _CDB_populate_the_geom_from_the_geom_webmercator(reloid REGCLASS, geom_columns_exist BOOLEAN[])
RETURNS void
AS $$
DECLARE
sql TEXT;
BEGIN
IF geom_columns_exist[2] AND NOT geom_columns_exist[1] THEN
sql := 'UPDATE ' || reloid::text || ' SET the_geom = ST_Transform(the_geom_webmercator, 4326) ';
EXECUTE sql;
END IF;
END;
$$ LANGUAGE PLPGSQL;
-- Initialize the_geom_webmercator with values from the_geom
-- do this only if the_geom was found (not created)
-- _and_ the_geom_webmercator was NOT found.
IF exists_geom_cols[1] AND NOT exists_geom_cols[2] THEN
-- 7) Initialize the_geom_webmercator with values from the_geom
-- do this only if the_geom was found (not created) and the_geom_webmercator was NOT found.
CREATE OR REPLACE FUNCTION _CDB_populate_the_geom_webmercator_from_the_geom(reloid REGCLASS, geom_columns_exist BOOLEAN[])
RETURNS void
AS $$
DECLARE
sql TEXT;
BEGIN
IF geom_columns_exist[1] AND NOT geom_columns_exist[2] THEN
sql := 'UPDATE ' || reloid::text || ' SET the_geom_webmercator = public.CDB_TransformToWebmercator(the_geom) ';
EXECUTE sql;
END IF;
END;
$$ LANGUAGE PLPGSQL;
-- Re-create all triggers
-- NOTE: drop/create has the side-effect of re-enabling disabled triggers
-- "track_updates"
-- 8) Create all triggers
-- NOTE: drop/create has the side-effect of re-enabling disabled triggers
CREATE OR REPLACE FUNCTION _CDB_create_triggers(schema_name TEXT, reloid REGCLASS)
RETURNS void
AS $$
DECLARE
sql TEXT;
BEGIN
-- "track_updates"
sql := 'CREATE trigger track_updates AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON '
|| reloid::text
|| ' FOR EACH STATEMENT EXECUTE PROCEDURE public.cdb_tablemetadata_trigger()';
|| reloid::text
|| ' FOR EACH STATEMENT EXECUTE PROCEDURE public.cdb_tablemetadata_trigger()';
EXECUTE sql;
-- "update_the_geom_webmercator"
-- TODO: why _before_ and not after ?
-- "update_the_geom_webmercator"
-- TODO: why _before_ and not after ?
sql := 'CREATE trigger update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON '
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public._CDB_update_the_geom_webmercator()';
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public._CDB_update_the_geom_webmercator()';
EXECUTE sql;
-- "update_updated_at"
-- TODO: why _before_ and not after ?
-- "update_updated_at"
-- TODO: why _before_ and not after ?
sql := 'CREATE trigger update_updated_at_trigger BEFORE UPDATE ON '
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public._CDB_update_updated_at()';
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public._CDB_update_updated_at()';
EXECUTE sql;
-- "test_quota" and "test_quota_per_row"
-- "test_quota" and "test_quota_per_row"
sql := 'CREATE TRIGGER test_quota BEFORE UPDATE OR INSERT ON '
|| reloid::text
|| ' EXECUTE PROCEDURE public.CDB_CheckQuota(1)';
|| reloid::text
|| ' EXECUTE PROCEDURE public.CDB_CheckQuota(1, ''-1'', '''
|| schema_name::text
|| ''')';
EXECUTE sql;
sql := 'CREATE TRIGGER test_quota_per_row BEFORE UPDATE OR INSERT ON '
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public.CDB_CheckQuota(0.001)';
|| reloid::text
|| ' FOR EACH ROW EXECUTE PROCEDURE public.CDB_CheckQuota(0.001, ''-1'', '''
|| schema_name::text
|| ''')';
EXECUTE sql;
END;
$$ LANGUAGE PLPGSQL;
-- Update the_geom_webmercator
CREATE OR REPLACE FUNCTION _CDB_update_the_geom_webmercator()
RETURNS trigger
AS $$
BEGIN
NEW.the_geom_webmercator := public.CDB_TransformToWebmercator(NEW.the_geom);
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION _CDB_update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;
-- ////////////////////////////////////////////////////
-- Ensure a table is a "cartodb" table
-- See https://github.com/CartoDB/cartodb/wiki/CartoDB-user-table
CREATE OR REPLACE FUNCTION CDB_CartodbfyTable(schema_name TEXT, reloid REGCLASS)
RETURNS void
AS $$
DECLARE
exists_geom_cols BOOLEAN[];
BEGIN
PERFORM cartodb._CDB_check_prerequisites(schema_name, reloid);
PERFORM cartodb._CDB_drop_triggers(reloid);
-- Ensure required fields exist
PERFORM cartodb._CDB_create_cartodb_id_column(reloid);
PERFORM cartodb._CDB_create_timestamp_columns(reloid);
SELECT cartodb._CDB_create_the_geom_columns(reloid) INTO exists_geom_cols;
-- Both only populate if proceeds
PERFORM cartodb._CDB_populate_the_geom_from_the_geom_webmercator(reloid, exists_geom_cols);
PERFORM cartodb._CDB_populate_the_geom_webmercator_from_the_geom(reloid, exists_geom_cols);
PERFORM cartodb._CDB_create_triggers(schema_name, reloid);
END;
$$ LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION CDB_CartodbfyTable(reloid REGCLASS)
RETURNS void
AS $$
BEGIN
PERFORM cartodb.CDB_CartodbfyTable('public', reloid);
END;
$$ LANGUAGE PLPGSQL;

View File

@@ -2,7 +2,7 @@
--
CREATE OR REPLACE FUNCTION CDB_DateToNumber(input timestamp)
RETURNS double precision AS $$
DECLARE output double precision DEFAULT NULL;
DECLARE output double precision;
BEGIN
BEGIN
SELECT extract (EPOCH FROM input) INTO output;

View File

@@ -11,3 +11,10 @@ BEGIN
EXECUTE sql;
END;
$$ language 'plpgsql' VOLATILE;
CREATE OR REPLACE FUNCTION cartodb.schema_exists(schema_name text)
RETURNS boolean AS
$$
SELECT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = schema_name::text);
$$
language sql VOLATILE;

View File

@@ -0,0 +1,90 @@
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Member_Group_Role_Member_Name()
RETURNS TEXT
AS 'SELECT ''cdb_org_member''::text || ''_'' || md5(current_database());'
LANGUAGE SQL IMMUTABLE;
DO LANGUAGE 'plpgsql' $$
DECLARE
cdb_org_member_role_name TEXT;
BEGIN
cdb_org_member_role_name := cartodb.CDB_Organization_Member_Group_Role_Member_Name();
IF NOT EXISTS ( SELECT * FROM pg_roles WHERE rolname= cdb_org_member_role_name )
THEN
EXECUTE 'CREATE ROLE "' || cdb_org_member_role_name || '" NOLOGIN;';
END IF;
END
$$;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Create_Member(role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'GRANT "' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || '" TO "' || role_name || '"';
END
$$ LANGUAGE PLPGSQL VOLATILE;
-------------------------------------------------------------------------------
-- Sharing tables
-------------------------------------------------------------------------------
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'GRANT USAGE ON SCHEMA "' || from_schema || '" TO "' || to_role_name || '"';
EXECUTE 'GRANT SELECT ON "' || from_schema || '".' || table_name || ' TO "' || to_role_name || '"';
END
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT cartodb.CDB_Organization_Add_Table_Read_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Write_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'GRANT USAGE ON SCHEMA "' || from_schema || '" TO "' || to_role_name || '"';
EXECUTE 'GRANT SELECT, INSERT, UPDATE, DELETE ON "' || from_schema || '".' || table_name || ' TO "' || to_role_name || '"';
END
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Write_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT cartodb.CDB_Organization_Add_Table_Read_Write_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Remove_Access_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'REVOKE ALL PRIVILEGES ON TABLE "' || from_schema || '".' || table_name || ' FROM "' || to_role_name || '"';
-- EXECUTE 'REVOKE USAGE ON SCHEMA ' || from_schema || ' FROM "' || to_role_name || '"';
-- We need to revoke usage on schema only if we are revoking privileges from the last table where to_role_name has
-- any permission granted within the schema from_schema
END
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION cartodb.CDB_Organization_Remove_Organization_Access_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT cartodb.CDB_Organization_Remove_Access_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE;

View File

@@ -22,7 +22,7 @@ BEGIN
END IF;
BEGIN
EXECUTE 'EXPLAIN (FORMAT XML) ' || rec.q INTO STRICT exp;
EXECUTE 'EXPLAIN (FORMAT XML, VERBOSE) ' || rec.q INTO STRICT exp;
EXCEPTION WHEN others THEN
-- TODO: if error is 'relation "xxxxxx" does not exist', take xxxxxx as
-- the affected table ?
@@ -33,14 +33,19 @@ BEGIN
-- Now need to extract all values of <Relation-Name>
--RAISE DEBUG 'Explain: %', exp;
-- RAISE DEBUG 'Explain: %', exp;
FOR rec2 IN WITH
inp AS ( SELECT xpath('//x:Relation-Name/text()', exp, ARRAY[ARRAY['x', 'http://www.postgresql.org/2009/explain']]) as x )
SELECT unnest(x)::name as p from inp
inp AS (
SELECT
xpath('//x:Relation-Name/text()', exp, ARRAY[ARRAY['x', 'http://www.postgresql.org/2009/explain']]) as x,
xpath('//x:Relation-Name/../x:Schema/text()', exp, ARRAY[ARRAY['x', 'http://www.postgresql.org/2009/explain']]) as s
)
SELECT unnest(x)::name as p, unnest(s)::name as sc from inp
LOOP
--RAISE DEBUG 'tab: %', rec2.p;
tables := array_append(tables, rec2.p);
-- RAISE DEBUG 'tab: %', rec2.p;
-- RAISE DEBUG 'sc: %', rec2.sc;
tables := array_append(tables, (rec2.sc || '.' || rec2.p)::name);
END LOOP;
-- RAISE DEBUG 'Tables: %', tables;

View File

@@ -1,5 +1,5 @@
-- Return the estimated size of user data. Used for quota checking.
CREATE OR REPLACE FUNCTION CDB_UserDataSize()
CREATE OR REPLACE FUNCTION CDB_UserDataSize(schema_name TEXT)
RETURNS bigint AS
$$
-- TODO: double check this query. Maybe use CDB_TableMetadata for lookup ?
@@ -7,55 +7,75 @@ $$
--
-- NOTE: division by 2 is an hack for the_geom_webmercator
--
SELECT coalesce(int8(sum(pg_total_relation_size(quote_ident(table_name))) / 2), 0)
SELECT coalesce(int8(sum(pg_total_relation_size(schema_name || '.' || table_name)) / 2), 0)
AS quota
FROM information_schema.tables
WHERE table_catalog = current_database() AND table_schema = 'public'
AND table_name != 'spatial_ref_sys'
AND table_name != 'cdb_tablemetadata'
AND table_type = 'BASE TABLE';
WHERE table_catalog = current_database() AND table_schema = schema_name
AND table_name != 'spatial_ref_sys'
AND table_name != 'cdb_tablemetadata'
AND table_type = 'BASE TABLE';
$$
LANGUAGE 'sql' VOLATILE;
-- Return the estimated size of user data. Used for quota checking.
-- Implicit schema version for backwards compatibility
CREATE OR REPLACE FUNCTION CDB_UserDataSize()
RETURNS bigint AS
$$
SELECT public.CDB_UserDataSize('public');
$$
LANGUAGE 'sql' VOLATILE;
-- Triggers cannot have declared arguments: pbfact float8, qmax int8, schema_name text
CREATE OR REPLACE FUNCTION CDB_CheckQuota()
RETURNS trigger AS
$$
DECLARE
pbfact float8;
qmax int8;
schema_name text;
dice float8;
quota float8;
BEGIN
IF TG_NARGS = 3 THEN
schema_name := TG_ARGV[2];
IF cartodb.schema_exists(schema_name) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
END IF;
ELSE
schema_name := 'public';
END IF;
-- By default try to use quota function, and if not present then rely on the one specified by params
BEGIN
EXECUTE FORMAT('SELECT %I._CDB_UserQuotaInBytes();', schema_name) INTO qmax;
EXCEPTION WHEN undefined_function THEN
BEGIN
IF TG_NARGS >= 2 AND TG_ARGV[1] <> '-1' THEN
qmax := TG_ARGV[1];
ELSE
RAISE EXCEPTION 'Missing "%"._CDB_UserQuotaInBytes()', schema_name;
END IF;
END;
END;
pbfact := TG_ARGV[0];
dice := random();
-- RAISE DEBUG 'CDB_CheckQuota enter: pbfact=% dice=%', pbfact, dice;
dice := random();
IF dice < pbfact THEN
RAISE DEBUG 'Checking quota on table % (dice:%, needed:<%)', TG_RELID::text, dice, pbfact;
BEGIN
qmax := public._CDB_UserQuotaInBytes();
EXCEPTION WHEN undefined_function THEN
IF TG_NARGS > 1 THEN
RAISE NOTICE 'Using quota specified via trigger parameter';
qmax := TG_ARGV[1];
ELSE
RAISE EXCEPTION 'Missing _CDB_UserQuotaInBytes(), and no quota provided as parameter';
END IF;
END;
IF qmax = 0 THEN
RETURN NEW;
END IF;
SELECT public.CDB_UserDataSize() INTO quota;
SELECT public.CDB_UserDataSize(schema_name) INTO quota;
IF quota > qmax THEN
RAISE EXCEPTION 'Quota exceeded by %KB', (quota-qmax)/1024;
RAISE EXCEPTION 'Quota exceeded by %KB', (quota-qmax)/1024;
ELSE RAISE DEBUG 'User quota in bytes: % < % (max allowed)', quota, qmax;
END IF;
-- ELSE RAISE DEBUG 'Not checking quota on table % (dice:%, needed:<%)', TG_RELID::text, dice, pbfact;
END IF;
RETURN NEW;
@@ -63,26 +83,33 @@ END;
$$
LANGUAGE 'plpgsql' VOLATILE;
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(bytes int8)
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
RETURNS int8 AS
$$
DECLARE
current_quota int8;
sql text;
BEGIN
BEGIN
current_quota := public._CDB_UserQuotaInBytes();
EXCEPTION WHEN undefined_function THEN
current_quota := 0;
END;
IF cartodb.schema_exists(schema_name::text) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name::text;
END IF;
sql := 'CREATE OR REPLACE FUNCTION public._CDB_UserQuotaInBytes() '
sql := 'CREATE OR REPLACE FUNCTION "' || schema_name::text || '"._CDB_UserQuotaInBytes() '
|| 'RETURNS int8 AS $X$ SELECT ' || bytes
|| '::int8 $X$ LANGUAGE sql IMMUTABLE';
EXECUTE sql;
return current_quota;
return bytes;
END
$$
LANGUAGE 'plpgsql' VOLATILE STRICT;
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(bytes int8)
RETURNS int8 AS
$$
BEGIN
return public.CDB_SetUserQuotaInBytes('public', bytes);
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT;

View File

@@ -2,7 +2,7 @@
--
CREATE OR REPLACE FUNCTION CDB_StringToDate(input character varying)
RETURNS date AS $$
DECLARE output DATE DEFAULT NULL;
DECLARE output DATE;
BEGIN
BEGIN
output := input::date;

View File

@@ -0,0 +1 @@
../scripts-available/CDB_Organizations.sql

View File

@@ -30,7 +30,7 @@ BEGIN
END IF;
END LOOP;
PERFORM CDB_CartodbfyTable(tabname);
PERFORM CDB_CartodbfyTable('public', tabname);
sql := 'INSERT INTO ' || tabname::text || '(the_geom) values ( CDB_LatLng(2,1) ) RETURNING cartodb_id';
EXECUTE sql INTO STRICT id;
@@ -132,7 +132,7 @@ LANGUAGE 'plpgsql';
-- table with single non-geometrical column
CREATE TABLE t AS SELECT 1::int as a;
SELECT CDB_CartodbfyTable('t'); -- should fail
SELECT CDB_CartodbfyTable('public', 't'); -- should fail
SELECT CDB_SetUserQuotaInBytes(0); -- Set user quota to infinite
SELECT CDB_CartodbfyTableCheck('t', 'single non-geometrical column');
DROP TABLE t;
@@ -200,6 +200,14 @@ SELECT CDB_CartodbfyTableCheck('t', 'unsequenced cartodb_id');
select cartodb_id FROM t;
DROP TABLE t;
-- table with existing cartodb_id serial primary key
CREATE TABLE t ( cartodb_id serial primary key );
SELECT CDB_CartodbfyTableCheck('t', 'cartodb_id serial primary key');
SELECT c.conname, a.attname FROM pg_constraint c, pg_attribute a
WHERE c.conrelid = 't'::regclass and a.attrelid = c.conrelid
AND c.conkey[1] = a.attnum AND NOT a.attisdropped;
DROP TABLE t;
-- table with existing the_geom and created_at and containing null values
-- Really, a test for surviving an longstanding PostgreSQL bug:
-- http://www.postgresql.org/message-id/20140530143150.GA11051@localhost

View File

@@ -2,6 +2,8 @@ SET
CREATE FUNCTION
SELECT 1
ERROR: Please set user quota before cartodbfying tables.
CONTEXT: SQL statement "SELECT cartodb._CDB_check_prerequisites(schema_name, reloid)"
PL/pgSQL function cdb_cartodbfytable(text,regclass) line 6 at PERFORM
0
single non-geometrical column cartodbfied fine
DROP TABLE
@@ -45,6 +47,10 @@ unsequenced cartodb_id cartodbfied fine
1
DROP TABLE
CREATE TABLE
cartodb_id serial primary key cartodbfied fine
t_pkey|cartodb_id
DROP TABLE
CREATE TABLE
null geom and timestamp values cartodbfied fine
DROP TABLE
DROP FUNCTION

View File

@@ -25,3 +25,9 @@ WITH inp AS ( select 'create table test (a int); insert into test values (1); se
WITH inp AS ( select 'WITH a AS (select * from pg_class) select * from a'::text as q )
SELECT q, CDB_QueryTables(q) from inp;
CREATE SCHEMA sc;
create table sc.test (a int);
insert into sc.test values (1);
WITH inp AS ( select 'select * from sc.test'::text as q )
SELECT q, CDB_QueryTables(q) from inp;

View File

@@ -1,11 +1,15 @@
SELECT * FROM geometry_columns|{pg_attribute,pg_class,pg_namespace,pg_type}
SELECT a.attname FROM pg_class c JOIN pg_attribute a on (a.attrelid = c.oid)|{pg_attribute,pg_class}
SELECT * FROM geometry_columns|{pg_catalog.pg_attribute,pg_catalog.pg_class,pg_catalog.pg_namespace,pg_catalog.pg_type}
SELECT a.attname FROM pg_class c JOIN pg_attribute a on (a.attrelid = c.oid)|{pg_catalog.pg_attribute,pg_catalog.pg_class}
CREATE table "my'tab;le" as select 1|{}
SELECT a.oid, b.oid FROM pg_class a, pg_class b|{pg_class}
SELECT a.oid, b.oid FROM pg_class a, pg_class b|{pg_catalog.pg_class}
SELECT 1 as col1; select 2 as col2|{}
WARNING: CDB_QueryTables cannot explain query: select 1 from nonexistant (42P01: relation "nonexistant" does not exist)
ERROR: relation "nonexistant" does not exist
begin; select * from pg_class; commit;|{pg_class}
begin; select * from pg_class; commit;|{pg_catalog.pg_class}
WARNING: CDB_QueryTables cannot explain query: select * from test (42P01: relation "test" does not exist)
ERROR: relation "test" does not exist
WITH a AS (select * from pg_class) select * from a|{pg_class}
WITH a AS (select * from pg_class) select * from a|{pg_catalog.pg_class}
CREATE SCHEMA
CREATE TABLE
INSERT 0 1
select * from sc.test|{sc.test}

View File

@@ -4,7 +4,7 @@ CREATE TABLE big(a int);
-- Try the legacy interface
-- See https://github.com/CartoDB/cartodb-postgresql/issues/13
CREATE TRIGGER test_quota BEFORE UPDATE OR INSERT ON big
EXECUTE PROCEDURE CDB_CheckQuota(1,1);
EXECUTE PROCEDURE CDB_CheckQuota(1, 1, 'public');
INSERT INTO big VALUES (1); -- allowed, check runs before
INSERT INTO big VALUES (1); -- disallowed, quota exceeds before
SELECT CDB_SetUserQuotaInBytes(0);

View File

@@ -6,9 +6,9 @@ ERROR: Quota exceeded by 3.9990234375KB
0
INSERT 0 1024
0
ERROR: Quota exceeded by 103.9921875KB
8
ERROR: Quota exceeded by 103.9921875KB
0
INSERT 0 1
DROP TABLE
SET

355
test/organization/test.sh Normal file
View File

@@ -0,0 +1,355 @@
#!/bin/sh
#
# It is expected that you run this script
# as a PostgreSQL superuser, for example:
#
# PGUSER=postgres bash ./test.sh
#
DATABASE=test_organizations
CMD='echo psql'
CMD=psql
OK=0
PARTIALOK=0
function set_failed() {
OK=1
PARTIALOK=1
}
function clear_partial_result() {
PARTIALOK=0
}
function sql() {
local ROLE
local QUERY
if [[ $# -ge 2 ]]
then
ROLE="$1"
QUERY="$2"
else
QUERY="$1"
fi
if [ -n "${ROLE}" ]; then
log_debug "Executing query '${QUERY}' as ${ROLE}"
RESULT=`${CMD} -U "${ROLE}" ${DATABASE} -c "${QUERY}" -A -t`
else
log_debug "Executing query '${QUERY}'"
RESULT=`${CMD} ${DATABASE} -c "${QUERY}" -A -t`
fi
CODERESULT=$?
echo ${RESULT}
echo
if [[ ${CODERESULT} -ne 0 ]]
then
echo -n "FAILED TO EXECUTE QUERY: "
log_warning "${QUERY}"
if [[ "$3" != "fails" ]]
then
log_error "${QUERY}"
set_failed
fi
else
if [[ "$3" == "fails" ]]
then
log_error "QUERY: '${QUERY}' was expected to fail and it did not fail"
set_failed
fi
fi
if [[ "$3" == "should" ]]
then
if [[ "${RESULT}" != "$4" ]]
then
log_error "QUERY '${QUERY}' expected result '${4}' but got '${RESULT}'"
set_failed
fi
fi
}
function log_info()
{
echo
echo
echo
_log "1;34m" "$1"
}
function log_error() {
_log "1;31m" "$1"
}
function log_debug() {
_log "1;32m" "> $1"
}
function log_warning() {
_log "0;33m" "$1"
}
function _log() {
echo -e "\033[$1$2\033[0m"
}
# '############################ HELPERS #############################'
function create_role_and_schema() {
local ROLE=$1
sql "CREATE ROLE ${ROLE} LOGIN;"
sql "GRANT CONNECT ON DATABASE \"${DATABASE}\" TO ${ROLE};"
sql "CREATE SCHEMA ${ROLE} AUTHORIZATION ${ROLE};"
sql "SELECT cartodb.CDB_Organization_Create_Member('${ROLE}')"
}
function drop_role_and_schema() {
local ROLE=$1
sql "DROP SCHEMA \"${ROLE}\";"
sql "REVOKE CONNECT ON DATABASE \"${DATABASE}\" FROM \"${ROLE}\";"
sql "DROP ROLE \"${ROLE}\";"
}
function create_table() {
if [[ $# -ne 2 ]]
then
log_error "create_table requires two arguments: role and table_name"
exit 1
fi
local ROLE="$1"
local TABLENAME="$2"
sql ${ROLE} "CREATE TABLE ${ROLE}.${TABLENAME} ( a int );"
}
function setup() {
${CMD} -c "CREATE DATABASE ${DATABASE}"
sql "CREATE SCHEMA cartodb;"
sql "GRANT USAGE ON SCHEMA cartodb TO public;"
log_info "########################### BOOTSTRAP ###########################"
${CMD} -d ${DATABASE} -f scripts-available/CDB_Organizations.sql
log_info "############################# SETUP #############################"
create_role_and_schema cdb_testmember_1
create_role_and_schema cdb_testmember_2
create_table cdb_testmember_1 foo
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo VALUES (1), (2), (3), (4), (5);'
sql cdb_testmember_1 'SELECT * FROM cdb_testmember_1.foo;'
create_table cdb_testmember_2 bar
sql cdb_testmember_2 'INSERT INTO bar VALUES (1), (2), (3), (4), (5);'
sql cdb_testmember_2 'SELECT * FROM cdb_testmember_2.bar;'
}
function tear_down() {
log_info "########################### USER TEAR DOWN ###########################"
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2');"
sql cdb_testmember_2 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_2', 'bar', 'cdb_testmember_1');"
sql cdb_testmember_1 'DROP TABLE cdb_testmember_1.foo;'
sql cdb_testmember_2 'DROP TABLE cdb_testmember_2.bar;'
sql "DROP SCHEMA cartodb CASCADE"
log_info "########################### TEAR DOWN ###########################"
sql 'DROP SCHEMA cdb_testmember_1;'
sql 'DROP SCHEMA cdb_testmember_2;'
sql "REVOKE CONNECT ON DATABASE \"${DATABASE}\" FROM cdb_testmember_1;"
sql "REVOKE CONNECT ON DATABASE \"${DATABASE}\" FROM cdb_testmember_2;"
sql 'DROP ROLE cdb_testmember_1;'
sql 'DROP ROLE cdb_testmember_2;'
${CMD} -c "DROP DATABASE ${DATABASE}"
}
function run_tests() {
local FAILED_TESTS=()
local TESTS
if [[ $# -ge 1 ]]
then
TESTS="$@"
else
TESTS=`cat $0 | perl -n -e'/function (test.*)\(\)/ && print "$1\n"'`
fi
for t in ${TESTS}
do
echo "####################################################################"
echo "#"
echo "# Running: ${t}"
echo "#"
echo "####################################################################"
clear_partial_result
setup
eval ${t}
if [[ ${PARTIALOK} -ne 0 ]]
then
FAILED_TESTS+=(${t})
fi
tear_down
done
if [[ ${OK} -ne 0 ]]
then
echo
log_error "The following tests are failing:"
printf -- '\t%s\n' "${FAILED_TESTS[@]}"
fi
}
#################################################### TESTS GO HERE ####################################################
function test_member_2_cannot_read_without_permission() {
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
}
function test_member_1_cannot_grant_read_permission_to_other_schema_than_its_one() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_2', 'foo', 'cdb_testmember_2')" fails
}
function test_member_1_grants_read_permission_and_member_2_can_read() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_2.bar;' fails
}
function test_member_2_cannot_add_table_to_member_1_schema_after_table_permission_added() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 "CREATE TABLE cdb_testmember_1.bar ( a int );" fails
}
function test_grant_read_permission_between_two_members() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_2', 'bar', 'cdb_testmember_1')"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_2.bar;' should 5
}
function test_member_2_cannot_write_to_member_1_table() {
sql cdb_testmember_2 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);' fails
}
function test_member_1_cannot_grant_read_write_permission_to_other_schema_than_its_one() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Write_Permission('cdb_testmember_2', 'foo', 'cdb_testmember_2')" fails
}
function test_member_2_can_write_to_member_1_table_after_write_permission_is_added() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Write_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);'
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 10
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 10
sql cdb_testmember_2 'DELETE FROM cdb_testmember_1.foo where a = 9;'
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 9
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 9
}
function test_member_1_removes_access_and_member_2_can_no_longer_query_the_table() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'SELECT * FROM cdb_testmember_1.foo;' fails
}
function test_member_1_removes_access_and_member_2_can_no_longer_write_to_the_table() {
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Write_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);'
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);' fails
}
function test_giving_permissions_to_two_tables_and_removing_from_first_table_should_not_remove_from_second() {
#### test setup
# create an extra table for cdb_testmember_1
create_table cdb_testmember_1 foo_2
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo_2 VALUES (1), (2), (3), (4), (5);'
sql cdb_testmember_1 'SELECT * FROM cdb_testmember_1.foo_2;'
# gives read permission to both tables
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo_2', 'cdb_testmember_2')"
# cdb_testmember_2 has access to both tables
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo_2;' should 5
# cdb_testmember_1 removes access to foo table
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
# cdb_testmember_2 should have access to foo_2 table but not to table foo
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo_2;' should 5
#### test tear down
sql cdb_testmember_1 'DROP TABLE cdb_testmember_1.foo_2;'
}
function test_cdb_org_member_role_allows_reading_to_all_users_without_explicit_permission() {
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Add_Table_Organization_Read_Permission('cdb_testmember_1', 'foo');"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
}
function test_user_can_read_when_it_has_permission_after_organization_permission_is_removed() {
create_role_and_schema cdb_testmember_3
# shares with cdb_testmember_2 and can read but cdb_testmember_3 cannot
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Add_Table_Read_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_3 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
# granting to organization allows to read to both: cdb_testmember_2 and cdb_testmember_3
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Add_Table_Organization_Read_Permission('cdb_testmember_1', 'foo');"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_3 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
# removing access from organization should keep permission on cdb_testmember_2 but drop it to cdb_testmember_3
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Remove_Organization_Access_Permission('cdb_testmember_1', 'foo');"
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
sql cdb_testmember_3 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
drop_role_and_schema cdb_testmember_3
}
function test_cdb_querytables_returns_schema_and_table_name() {
sql "CREATE EXTENSION plpythonu;"
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryStatements.sql
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryTables.sql
sql cdb_testmember_1 "select * from CDB_QueryTables('select * from foo');" should "{cdb_testmember_1.foo}"
}
function test_cdb_querytables_returns_schema_and_table_name_for_several_schemas() {
sql "CREATE EXTENSION plpythonu;"
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryStatements.sql
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryTables.sql
sql postgres "select * from CDB_QueryTables('select * from cdb_testmember_1.foo, cdb_testmember_2.bar');" should "{cdb_testmember_1.foo,cdb_testmember_2.bar}"
}
function test_cdb_querytables_does_not_return_functions_as_part_of_the_resultset() {
sql "CREATE EXTENSION plpythonu;"
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryStatements.sql
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryTables.sql
sql postgres "select * from CDB_QueryTables('select * from cdb_testmember_1.foo, cdb_testmember_2.bar, plainto_tsquery(''foo'')');" should "{cdb_testmember_1.foo,cdb_testmember_2.bar}"
}
#################################################### TESTS END HERE ####################################################
run_tests $@
exit ${OK}

View File

@@ -4,7 +4,22 @@ ver=$1
input=cartodb--${ver}.sql
output=cartodb--unpackaged--${ver}.sql
cat ${input} | grep -v 'duplicated extension$' > ${output}
echo "-- Script generated by $0 on `date`" > ${output}
# Migrate CDB functions from public schema to cartodb schema
cat ${input} |
grep '^ *CREATE OR REPLACE FUNCTION' |
grep -v ' cartodb\.' | # should only match DDL hooks
sed 's/).*$/)/' |
sed 's/DEFAULT [^ ,)]*//g' |
sed 's/CREATE OR REPLACE FUNCTION /ALTER FUNCTION public./' |
sed 's/$/ SET SCHEMA cartodb;/' |
sed 's/^/DO LANGUAGE plpgsql \$\$ BEGIN /' |
sed "s/$/ EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'Got % (%)', SQLERRM, SQLSTATE; END; \$\$;/" |
cat >> ${output}
# Upgrade all functions
cat ${input} | grep -v 'duplicated extension$' >> ${output}
# Migrate CDB_TableMetadata
cat >> ${output} <<'EOF'
@@ -29,32 +44,34 @@ BEGIN
DROP FUNCTION public._CDB_UserQuotaInBytes();
END;
$$ LANGUAGE 'plpgsql';
-- Cartodbfy tables with a trigger using 'CDB_CheckQuota' or
-- 'CDB_TableMetadata_Trigger' from the 'public' schema
select cartodb.CDB_CartodbfyTable(relname::regclass) from (
-- names of tables using public.CDB_CheckQuota or
-- public.CDB_TableMetadata_Trigger in their triggers
SELECT distinct c.relname
FROM
pg_trigger t,
pg_class c,
pg_proc p,
pg_namespace n
WHERE
n.nspname = 'public' AND
p.pronamespace = n.oid AND
p.proname IN ( 'cdb_checkquota', 'cdb_tablemetadata_trigger' ) AND
t.tgrelid = c.oid AND
p.oid = t.tgfoid
) as foo;
EOF
# Drop functions from public schema
cat ${input} |
grep '^ *CREATE OR REPLACE FUNCTION' |
grep -v ' cartodb\.' | # should only match DDL hooks
sed 's/).*$/);/' |
sed 's/DEFAULT [^ ,)]*//g' |
sed 's/CREATE OR REPLACE FUNCTION /DROP FUNCTION public./' |
cat >> ${output}
## Cartodbfy tables with a trigger using 'CDB_CheckQuota' or
## 'CDB_TableMetadata_Trigger' from the 'public' schema
#cat >> ${output} <<'EOF'
#select cartodb.CDB_CartodbfyTable(relname::regclass) from (
# -- names of tables using public.CDB_CheckQuota or
# -- public.CDB_TableMetadata_Trigger in their triggers
# SELECT distinct c.relname
# FROM
# pg_trigger t,
# pg_class c,
# pg_proc p,
# pg_namespace n
# WHERE
# n.nspname = 'public' AND
# p.pronamespace = n.oid AND
# p.proname IN ( 'cdb_checkquota', 'cdb_tablemetadata_trigger' ) AND
# t.tgrelid = c.oid AND
# p.oid = t.tgfoid
#) as foo;
#EOF
## Drop any leftover function from public schema (there should be none)
#cat ${input} |
# grep '^ *CREATE OR REPLACE FUNCTION' |
# grep -v ' cartodb\.' | # should only match DDL hooks
# sed 's/).*$/);/' |
# sed 's/DEFAULT [^ ,)]*//g' |
# sed 's/CREATE OR REPLACE FUNCTION /DROP FUNCTION IF EXISTS public./' |
# cat >> ${output}