Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afa52aa92b | ||
|
|
746dbea434 | ||
|
|
f9bd469ea9 | ||
|
|
402d97daa6 | ||
|
|
e41d2ec019 | ||
|
|
3c460f1a85 | ||
|
|
076207c49c | ||
|
|
ce1e9ac41c | ||
|
|
0f33ee8b22 | ||
|
|
a32dea0282 | ||
|
|
3a255df9d0 | ||
|
|
c4e2549dc8 | ||
|
|
2e9f642378 | ||
|
|
99096d41e0 | ||
|
|
3a10ef7e76 | ||
|
|
a20676f391 | ||
|
|
37004db047 | ||
|
|
1189d70b2a | ||
|
|
8cfc8e65cf | ||
|
|
32db4fd81e | ||
|
|
a1e3e9a8df | ||
|
|
6e34e16b8d | ||
|
|
70220e04c1 | ||
|
|
a5cb9f268d | ||
|
|
d2d909145d | ||
|
|
34dec227c4 | ||
|
|
99e92e2505 | ||
|
|
c58a084102 | ||
|
|
b7907ff82f | ||
|
|
12d955075a | ||
|
|
10a4d85c01 | ||
|
|
524bb6ad42 | ||
|
|
4da89d8abd | ||
|
|
c7311ba48e | ||
|
|
2eae7876e2 | ||
|
|
91c3b86d45 | ||
|
|
f55d789c41 | ||
|
|
3eb8ab24d8 | ||
|
|
0f1c98c743 | ||
|
|
8ecd2cd5e2 | ||
|
|
f4be59cae0 | ||
|
|
fe66b2865a | ||
|
|
2be9d2d81a | ||
|
|
5744921065 | ||
|
|
4c93258cd2 | ||
|
|
bf140890d8 | ||
|
|
29a31d4c40 | ||
|
|
dbd403a2f6 | ||
|
|
cb353ec6a8 | ||
|
|
2beabfced6 | ||
|
|
7bdee5c13e | ||
|
|
c07784566a | ||
|
|
2e1fe2933c | ||
|
|
d378ca6fe0 | ||
|
|
446f4113d9 | ||
|
|
5963c67376 | ||
|
|
f5f18ca57c | ||
|
|
d8c840d126 | ||
|
|
65483743b4 | ||
|
|
4651883454 | ||
|
|
470bae6268 | ||
|
|
75ba13b834 | ||
|
|
057695361d | ||
|
|
0e1aeb0a76 | ||
|
|
f2dae651b3 | ||
|
|
9f414938ba | ||
|
|
4f7b07f922 | ||
|
|
69cc56b589 | ||
|
|
1028b24333 | ||
|
|
4d1c4f6a22 | ||
|
|
83ab128a01 | ||
|
|
b0b4a92240 | ||
|
|
c06d24aa19 | ||
|
|
4f4df2de8d | ||
|
|
b5f36902c5 | ||
|
|
0bcbf6708a | ||
|
|
2e665a56b4 | ||
|
|
1e8ef2e1d9 | ||
|
|
e6ecde4346 | ||
|
|
2d42e6197a | ||
|
|
5605fdd9b2 | ||
|
|
ab7082d4c3 | ||
|
|
f9ac627e0e | ||
|
|
cc2066ea89 | ||
|
|
aa380171b4 | ||
|
|
a43abb37e0 | ||
|
|
787b513715 | ||
|
|
362af5e6a0 | ||
|
|
8dbd797429 | ||
|
|
57ac26cbe7 | ||
|
|
010cbe6e18 | ||
|
|
8e68f2a0a7 | ||
|
|
52aab9d564 | ||
|
|
06f563bb73 | ||
|
|
78077a6ec1 | ||
|
|
c45ef6c540 | ||
|
|
42dc03d77b | ||
|
|
9254723719 | ||
|
|
26ad966ab6 | ||
|
|
a2723a3c90 | ||
|
|
2f8ea7e4ea | ||
|
|
45c21d7f42 | ||
|
|
f442c21fa4 | ||
|
|
ee9d08a2be | ||
|
|
7606585672 | ||
|
|
81d0f338cf | ||
|
|
951f257654 | ||
|
|
f461faf0b6 | ||
|
|
a8d57abda6 | ||
|
|
982ddfdeff | ||
|
|
da4331ac78 | ||
|
|
07d43275f8 | ||
|
|
ec38444b7d | ||
|
|
ee9010dc23 | ||
|
|
74210c5b5c | ||
|
|
d14d1d9994 | ||
|
|
5b19d0fc5e | ||
|
|
2baee24f30 | ||
|
|
85d6164956 | ||
|
|
a5b2b66bb6 | ||
|
|
3faa389860 | ||
|
|
bc5d532735 | ||
|
|
4f3d19ce7a | ||
|
|
45fed9cf1b | ||
|
|
e19489144c | ||
|
|
83707297de | ||
|
|
2f40261b8d | ||
|
|
b1202011f6 | ||
|
|
65d51fd8bd | ||
|
|
1a271d977b | ||
|
|
9c6294d95b | ||
|
|
c7bba14e9a | ||
|
|
667f896cfb | ||
|
|
cc1df0a708 | ||
|
|
d1ee383d9b | ||
|
|
a794fb3d31 | ||
|
|
50e41179fc | ||
|
|
e3138cd56a | ||
|
|
ab6720ad32 | ||
|
|
5f154a5859 | ||
|
|
99dd7cefc7 | ||
|
|
85997e2445 | ||
|
|
1bd4b9a6e3 | ||
|
|
5d4f1d98d7 | ||
|
|
1637257772 | ||
|
|
a904b101a3 | ||
|
|
917a975baa | ||
|
|
0568b36a90 | ||
|
|
6d122462bb | ||
|
|
ffaf5e4400 | ||
|
|
2a4ecd4850 | ||
|
|
3f4479fe12 | ||
|
|
34032964c0 | ||
|
|
6c57751579 | ||
|
|
9a3ac908a9 | ||
|
|
80fdd00541 | ||
|
|
aeec2bbe06 | ||
|
|
5033a0ba35 | ||
|
|
d2a00852a8 | ||
|
|
7e131ac88d | ||
|
|
e374b9128f | ||
|
|
e605234d38 | ||
|
|
272d5be776 | ||
|
|
0e7d797400 | ||
|
|
fcc06e82bf | ||
|
|
60a21d34bf | ||
|
|
74f939fd53 | ||
|
|
7e131143f9 | ||
|
|
ac15a6da25 | ||
|
|
c0a7714f33 | ||
|
|
0c864694b6 | ||
|
|
4b246d34c8 | ||
|
|
87454ac37f | ||
|
|
e3837c603d | ||
|
|
e84832b7f4 | ||
|
|
b209726b1c | ||
|
|
51a669f93c | ||
|
|
c8a1119556 | ||
|
|
24a37be1a9 | ||
|
|
5659275c0c | ||
|
|
7760d6b30d | ||
|
|
4515c8547e | ||
|
|
2766bbc83a | ||
|
|
c4980a90f9 | ||
|
|
61d2024eb5 | ||
|
|
af142306aa | ||
|
|
7437a9686b | ||
|
|
82f90e618c | ||
|
|
55a77b0ef0 | ||
|
|
2e68626165 | ||
|
|
06b7eb8504 | ||
|
|
ccbabaa3b4 | ||
|
|
5c86029029 | ||
|
|
acd101af9b | ||
|
|
d811a71da0 |
68
.travis.yml
68
.travis.yml
@@ -1,46 +1,38 @@
|
||||
dist: xenial
|
||||
language: c
|
||||
dist: precise
|
||||
sudo: required
|
||||
|
||||
addons:
|
||||
postgresql: 9.5
|
||||
env:
|
||||
global:
|
||||
- PGUSER=postgres
|
||||
- PGDATABASE=postgres
|
||||
- PGOPTIONS='-c client_min_messages=NOTICE'
|
||||
- PGPORT=5432
|
||||
- POSTGIS_VERSION="2.5"
|
||||
|
||||
matrix:
|
||||
- POSTGRESQL_VERSION="9.6"
|
||||
- POSTGRESQL_VERSION="10"
|
||||
- POSTGRESQL_VERSION="11"
|
||||
|
||||
|
||||
before_install:
|
||||
# Add custom PPAs from cartodb
|
||||
- sudo add-apt-repository -y ppa:cartodb/postgresql-9.5
|
||||
- sudo add-apt-repository -y ppa:cartodb/gis
|
||||
- sudo add-apt-repository -y ppa:cartodb/gis-testing
|
||||
- sudo apt-get update
|
||||
|
||||
# Force instalation of libgeos-3.5.0 (presumably needed because of existing version of postgis)
|
||||
- sudo apt-get -y install libgeos-3.5.0=3.5.0-1cdb2
|
||||
|
||||
# Install postgres db and build deps
|
||||
- sudo /etc/init.d/postgresql stop # stop travis default instance
|
||||
- sudo apt-get -y remove --purge postgresql-9.1
|
||||
- sudo apt-get -y remove --purge postgresql-9.2
|
||||
- sudo apt-get -y remove --purge postgresql-9.3
|
||||
- sudo apt-get -y remove --purge postgresql-9.4
|
||||
- sudo apt-get -y remove --purge postgresql-9.5
|
||||
- sudo rm -rf /var/lib/postgresql/
|
||||
- sudo rm -rf /var/log/postgresql/
|
||||
- sudo rm -rf /etc/postgresql/
|
||||
- sudo apt-get -y remove --purge postgis-2.2
|
||||
- sudo apt-get -y autoremove
|
||||
|
||||
- sudo apt-get -y install postgresql-9.5=9.5.2-3cdb3
|
||||
- sudo apt-get -y install postgresql-server-dev-9.5=9.5.2-3cdb3
|
||||
- sudo apt-get -y install postgresql-plpython-9.5=9.5.2-3cdb3
|
||||
- sudo apt-get -y install postgresql-9.5-postgis-scripts=2.2.2.0-cdb2
|
||||
- sudo apt-get -y install postgresql-9.5-postgis-2.2=2.2.2.0-cdb2
|
||||
|
||||
# configure it to accept local connections from postgres
|
||||
- echo -e "# TYPE DATABASE USER ADDRESS METHOD \nlocal all postgres trust\nlocal all all trust\nhost all all 127.0.0.1/32 trust" \
|
||||
| sudo tee /etc/postgresql/9.5/main/pg_hba.conf
|
||||
- sudo /etc/init.d/postgresql restart 9.5
|
||||
|
||||
- sudo service postgresql stop;
|
||||
- sudo apt-get remove postgresql* -y
|
||||
- sudo apt-get install -y --allow-unauthenticated --no-install-recommends --no-install-suggests postgresql-$POSTGRESQL_VERSION postgresql-client-$POSTGRESQL_VERSION postgresql-server-dev-$POSTGRESQL_VERSION postgresql-common
|
||||
- if [[ $POSTGRESQL_VERSION == '9.6' ]]; then sudo apt-get install -y postgresql-contrib-9.6; fi;
|
||||
- sudo apt-get install -y --allow-unauthenticated postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION-scripts postgis postgresql-plpython-$POSTGRESQL_VERSION
|
||||
- sudo pg_dropcluster --stop $POSTGRESQL_VERSION main
|
||||
- sudo rm -rf /etc/postgresql/$POSTGRESQL_VERSION /var/lib/postgresql/$POSTGRESQL_VERSION
|
||||
- sudo pg_createcluster -u postgres $POSTGRESQL_VERSION main -- --auth-local trust --auth-host password
|
||||
- sudo /etc/init.d/postgresql start $POSTGRESQL_VERSION || sudo journalctl -xe
|
||||
- sudo pip install redis==2.4.9
|
||||
script:
|
||||
- make
|
||||
- sudo make install
|
||||
- PGOPTIONS='-c client_min_messages=NOTICE' PGUSER=postgres make installcheck ||
|
||||
{ cat regression.diffs; false; }
|
||||
- make installcheck
|
||||
|
||||
after_failure:
|
||||
- pg_lsclusters
|
||||
- cat regression.out
|
||||
- cat regression.diffs
|
||||
|
||||
@@ -24,14 +24,13 @@ version of objects in other cases.
|
||||
|
||||
When adding a new function or modifying an exiting one make sure that the
|
||||
[VOLATILITY](https://www.postgresql.org/docs/current/static/xfunc-volatility.html) and [PARALLEL](https://www.postgresql.org/docs/9.6/static/parallel-safety.html) categories are updated accordingly.
|
||||
As PARALLEL labels need to be stripped for incompatible PostgreSQL versions
|
||||
please use _PARALLEL SAFE/RESTRICTED/UNSAFE_ in uppercase so it's handled
|
||||
automatically.
|
||||
|
||||
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.
|
||||
|
||||
Although the extension will usually be installed in the "cartodb" schema, please
|
||||
use @extschema@ to fully-qualify internal calls to avoid name clashes.
|
||||
When you use postgis functions or types, please fully-qualify them by using
|
||||
@postgisschema@ (it's changed to "public" by the install script) to avoid
|
||||
pg_upgrade issues.
|
||||
|
||||
Every new feature (as well as bugfixes) should come with a test case,
|
||||
see the 'Writing testcases' section.
|
||||
|
||||
46
Makefile
46
Makefile
@@ -1,7 +1,7 @@
|
||||
# cartodb/Makefile
|
||||
|
||||
EXTENSION = cartodb
|
||||
EXTVERSION = 0.22.1
|
||||
EXTVERSION = 0.30.0
|
||||
|
||||
SED = sed
|
||||
AWK = awk
|
||||
@@ -87,6 +87,22 @@ UPGRADABLE = \
|
||||
0.21.0 \
|
||||
0.22.0 \
|
||||
0.22.1 \
|
||||
0.22.2 \
|
||||
0.23.0 \
|
||||
0.23.1 \
|
||||
0.23.2 \
|
||||
0.24.0 \
|
||||
0.24.1 \
|
||||
0.25.0 \
|
||||
0.26.0 \
|
||||
0.26.1 \
|
||||
0.27.0 \
|
||||
0.27.1 \
|
||||
0.27.2 \
|
||||
0.28.0 \
|
||||
0.28.1 \
|
||||
0.29.0 \
|
||||
0.30.0 \
|
||||
$(EXTVERSION)dev \
|
||||
$(EXTVERSION)next \
|
||||
$(END)
|
||||
@@ -114,24 +130,17 @@ REGRESS = test_setup $(REGRESS_LEGACY)
|
||||
|
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
PG_PARALLEL := $(shell $(PG_CONFIG) --version | ($(AWK) '{$$2*=1000; if ($$2 >= 9600) print 1; else print 0;}' 2> /dev/null || echo 0))
|
||||
include $(PGXS)
|
||||
|
||||
PG_VERSION := $(shell $(PG_CONFIG) --version | $(AWK) '{split($$2,a,"."); print a[1]}')
|
||||
|
||||
$(EXTENSION)--$(EXTVERSION).sql: $(CDBSCRIPTS) cartodb_version.sql Makefile
|
||||
echo '\echo Use "CREATE EXTENSION $(EXTENSION)" to load this file. \quit' > $@
|
||||
cat $(CDBSCRIPTS) | \
|
||||
$(SED) -e 's/public\./cartodb./g' \
|
||||
-e 's/:DATABASE_USERNAME/cdb_org_admin/g' \
|
||||
-e "s/''public''/''cartodb''/g" >> $@
|
||||
$(SED) -e 's/@extschema@/cartodb/g' \
|
||||
-e "s/@postgisschema@/public/g" >> $@
|
||||
echo "GRANT USAGE ON SCHEMA cartodb TO public;" >> $@
|
||||
cat cartodb_version.sql >> $@
|
||||
ifeq ($(PG_PARALLEL), 0)
|
||||
# Remove PARALLEL in aggregates and functions
|
||||
$(eval TMPFILE := $(shell mktemp /tmp/$(basename $0).XXXXXXXX))
|
||||
$(SED) -e 's/PARALLEL \= [A-Z]*,/''/g' \
|
||||
-e 's/PARALLEL [A-Z]*/''/g' $@ > $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
endif
|
||||
|
||||
$(EXTENSION)--unpackaged--$(EXTVERSION).sql: $(EXTENSION)--$(EXTVERSION).sql util/create_from_unpackaged.sh Makefile
|
||||
./util/create_from_unpackaged.sh $(EXTVERSION)
|
||||
@@ -144,12 +153,9 @@ $(EXTENSION)--$(EXTVERSION)--$(EXTVERSION)next.sql: $(EXTENSION)--$(EXTVERSION).
|
||||
|
||||
$(EXTENSION).control: $(EXTENSION).control.in Makefile
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
|
||||
ifeq ($(PG_PARALLEL), 0)
|
||||
echo -e "\033[0;31mExtension created without PARALLEL support\033[0m"
|
||||
endif
|
||||
|
||||
cartodb_version.sql: cartodb_version.sql.in Makefile $(GITDIR)/index
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' -e 's/@extschema@/cartodb/g' -e "s/@postgisschema@/public/g" $< > $@
|
||||
|
||||
# Needed for consistent `echo` results with backslashes
|
||||
SHELL = bash
|
||||
@@ -166,10 +172,14 @@ legacy_regress: $(REGRESS_OLD) Makefile
|
||||
echo '\t' >> $${of}; \
|
||||
echo '\set QUIET off' >> $${of}; \
|
||||
cat $${f} | \
|
||||
$(SED) -e 's/public\./cartodb./g' >> $${of}; \
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' -e 's/@extschema@/cartodb/g' -e "s/@postgisschema@/public/g" >> $${of}; \
|
||||
exp=expected/test/$${tn}.out; \
|
||||
echo '\set ECHO none' > $${exp}; \
|
||||
cat test/$${tn}_expect >> $${exp}; \
|
||||
if [[ -f "test/$${tn}_expect.pg$(PG_VERSION)" ]]; then \
|
||||
cat test/$${tn}_expect.pg$(PG_VERSION) >> $${exp}; \
|
||||
else \
|
||||
cat test/$${tn}_expect >> $${exp}; \
|
||||
fi \
|
||||
done
|
||||
|
||||
test_organization:
|
||||
|
||||
65
NEWS.md
65
NEWS.md
@@ -1,3 +1,68 @@
|
||||
0.30.0 (2019-07-17)
|
||||
* Added new admin functions to connect CARTO with user FDW's (#369)
|
||||
|
||||
0.29.0 (2019-07-15)
|
||||
* Added new function CDB_OAuth:
|
||||
* Install event trigger to check for table/view/sequence/function creation
|
||||
* Reassign the ownership of new objects to a defined role in the cdb_conf
|
||||
* Changed MakeFile to support different expects for differents PG versions
|
||||
|
||||
0.28.1 (2019-07-04)
|
||||
* Avoid temporary tables creation in CDB_SyncTable (#366)
|
||||
* Make CDB_Get_Foreign_Updated_At robust to missing CDB_TableMetadata (#362)
|
||||
|
||||
0.28.0 (2019-07-01)
|
||||
* New function CDB_SyncTable (#355)
|
||||
|
||||
0.27.2 (2019-06-21)
|
||||
* Improvements and fixes in Ghost tables functions (#360)
|
||||
|
||||
0.27.1 (2019-06-03)
|
||||
* Add some qualifications that were left in the previous release.
|
||||
|
||||
0.27.0 (2019-06-03)
|
||||
* Fully qualify function calls
|
||||
* Several improvements to bash tests.
|
||||
* Avoid dropping publicuser in tests.
|
||||
* Raise minimum requirement to PostgreSQL 9.6.
|
||||
|
||||
0.26.1 (2019-03-19)
|
||||
* Remove default TIS values from Ghost tables functions
|
||||
|
||||
0.26.0 (2019-03-11)
|
||||
* Use `ST_ShiftLongitude` instead of `ST_Shift_Longitude`.
|
||||
* Add Ghost tables functions to install triggers and enqueue the linking process
|
||||
|
||||
0.25.0 (2019-02-22)
|
||||
* Add `CDB_Username` to get the cartodb username from the current PostgreSQL user
|
||||
|
||||
0.24.1 (2019-01-02)
|
||||
* Drop functions removed in 0.12 (#341)
|
||||
* Travis: Test with PostgreSQL 9.5, 10 and 11.
|
||||
|
||||
0.24.0 (2018-09-13)
|
||||
* Travis: Test with PostgreSQL 9.5 and 10.
|
||||
* _cdb_estimated_extent: Fix bug with ST_EstimatedExtent interaction.
|
||||
* Improvements in `CDB_JenksBins`.
|
||||
* Now it ignores NULLs.
|
||||
* No longer puts the same value in multiple categories.
|
||||
* Removes all limits related to size.
|
||||
* If not set, the number of iterations done is based now on the size of the array.
|
||||
* Fixed multiple bugs.
|
||||
* The internal function `CDB_JenksBinsIteration` has changed its signature.
|
||||
|
||||
0.23.2 (2018-07-19)
|
||||
* Fix `CDB_QueryTablesText` with parenthesized queries (#335)
|
||||
|
||||
0.23.1 (2018-07-19)
|
||||
* Fix `CDB_EstimateRowCount` parallelizability #333
|
||||
|
||||
0.23.0 (2018-07-03)
|
||||
* Add a new helper function `_CDB_Table_Exists(table_name_with_optional_schema TEXT)` #332
|
||||
|
||||
0.22.2 (2018-05-29)
|
||||
* Fix: Fix hyphenates usernames in 0.22.1 fix (#331)
|
||||
|
||||
0.22.1 (2018-05-29)
|
||||
* Fix: Correctly grant permission to all sequences related with table (#330)
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ See [the cartodb-postgresql wiki](https://github.com/CartoDB/cartodb-postgresql/
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* PostgreSQL 9.4+ (with plpythonu extension and xml support)
|
||||
* [PostGIS extension](http://postgis.net)
|
||||
* PostgreSQL 9.6+ (with plpythonu extension and xml support)
|
||||
* [PostGIS extension](http://postgis.net)
|
||||
* Python with [Redis module](https://pypi.org/project/redis/)
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
11
carto-package.json
Normal file
11
carto-package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "carto_postgresql_ext",
|
||||
"current_version": {
|
||||
"requires": {
|
||||
"postgresql": ">=10.0.0",
|
||||
"postgis": ">=2.4.0.0"
|
||||
},
|
||||
"works_with": {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
DO $$ BEGIN IF EXISTS (SELECT * FROM pg_proc p, pg_namespace n WHERE p.proname = 'cdb_transformtowebmercator' AND p.pronamespace = n.oid AND n.nspname = 'public') THEN RAISE EXCEPTION 'Use CREATE EXTENSION cartodb FROM unpackaged'; END IF; END; $$ LANGUAGE 'plpgsql'; -- forbid duplicated extension
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_version()
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_version()
|
||||
RETURNS text AS $$
|
||||
SELECT '@@VERSION@@'::text;
|
||||
$$ language 'sql' IMMUTABLE STRICT;
|
||||
|
||||
56
doc/CDB_SyncTable.md
Normal file
56
doc/CDB_SyncTable.md
Normal file
@@ -0,0 +1,56 @@
|
||||
Synchronize two tables. This function will synchronize a *destination* table with a *source* table.
|
||||
The idea is that the *destination* is a replica of *source* and *source* has been subject to
|
||||
modifications that are to be applied to *destination*.
|
||||
|
||||
This will be achieved by deleting the rows in the destination not present
|
||||
in the source, inserting rows of the source not in the destination and updating modified rows.
|
||||
If the destination table does not exist it will be created and all the rows of the source inserted into it.
|
||||
|
||||
Both tables must have a consistent `cartodb_id` primary key column which will be used to match
|
||||
the source and destination rows.
|
||||
|
||||
Note that both tables do not necessarily become identical after the synchronization, since additional columns
|
||||
may have been added to the destination; those columns will not be altered by the synchronization.
|
||||
|
||||
In addition some source columns may be skipped by listing them in the optional last argument; such columns
|
||||
will not be updated in the destination, so if they are present in it their values won't be altered.
|
||||
|
||||
|
||||
#### Using the function
|
||||
|
||||
Import some data using COPY FROM into a temporary table, then synchronize a table with the data and
|
||||
finally delete the temporary table. This could be used import and update some data periodically while
|
||||
allowing to add columns to the data that will be preserved across updates.
|
||||
|
||||
```sql
|
||||
CREATE tmp_pois(cartodb_id int, name text, type text, longitude double precision, latitude double precision, rank int);
|
||||
COPY tmp_pois FROM '/tmp/pois.csv';
|
||||
SELECT CDB_SyncTable('tmp_pois', 'public', 'pois');
|
||||
DROP TABLE tmp_pois;
|
||||
```
|
||||
|
||||
Now we could perform some changes to the `pois` to maintain our own ranking:
|
||||
|
||||
```sql
|
||||
UPDATE pois SET rank = random()*4 + 1;
|
||||
```
|
||||
|
||||
Then, if the source were updated at `/tmp/pois.csv` we could synchronize with it while preserving our `rank` values with:
|
||||
|
||||
```sql
|
||||
CREATE tmp_pois(cartodb_id int, name text, type text, longitude double precision, latitude double precision, rank int);
|
||||
COPY tmp_pois FROM '/tmp/pois.csv';
|
||||
SELECT CDB_SyncTable('tmp_pois', 'public', 'pois', '{rank}');
|
||||
DROP TABLE tmp_pois;
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
```
|
||||
CDB_SyncTable(src_table, dst_schema, dst_table, skip_cols)
|
||||
```
|
||||
|
||||
* **src_table** REGCLASS the source data for the synchronization
|
||||
* **dst_scgena** REGNAMESPACE the destination schema
|
||||
* **dst_table** NAME the destination table to be updated
|
||||
* **skip_cols** NAME[] an array of column names, empty by default, which will be skipped
|
||||
@@ -1,6 +1,6 @@
|
||||
-- Table to register analysis nodes from https://github.com/cartodb/camshaft
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
cartodb.cdb_analysis_catalog (
|
||||
@extschema@.cdb_analysis_catalog (
|
||||
-- md5 hex hash
|
||||
node_id char(40) CONSTRAINT cdb_analysis_catalog_pkey PRIMARY KEY,
|
||||
-- being json allows to do queries like analysis_def->>'type' = 'buffer'
|
||||
@@ -34,7 +34,7 @@ cartodb.cdb_analysis_catalog (
|
||||
-- This can only be called from an SQL script executed by CREATE EXTENSION
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
PERFORM pg_catalog.pg_extension_config_dump('cartodb.cdb_analysis_catalog', '');
|
||||
PERFORM pg_catalog.pg_extension_config_dump('@extschema@.cdb_analysis_catalog', '');
|
||||
END
|
||||
$$;
|
||||
|
||||
@@ -45,7 +45,7 @@ $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN last_modified_by uuid;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN last_modified_by uuid;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
@@ -54,7 +54,7 @@ $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN last_error_message text;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN last_error_message text;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
@@ -63,7 +63,7 @@ $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN cache_tables regclass[] NOT NULL DEFAULT '{}';
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN cache_tables regclass[] NOT NULL DEFAULT '{}';
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
@@ -72,7 +72,7 @@ $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN username text;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN username text;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
@@ -84,12 +84,12 @@ DO LANGUAGE 'plpgsql' $$
|
||||
DECLARE
|
||||
column_index int;
|
||||
BEGIN
|
||||
SELECT ordinal_position FROM information_schema.columns WHERE table_name='cdb_analysis_catalog' AND table_schema='cartodb' AND column_name='username' INTO column_index;
|
||||
SELECT ordinal_position FROM information_schema.columns WHERE table_name='cdb_analysis_catalog' AND table_schema='@extschema@' AND column_name='username' INTO column_index;
|
||||
IF column_index = 1 OR column_index = 10 THEN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN username_final text;
|
||||
UPDATE cartodb.cdb_analysis_catalog SET username_final = username;
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog DROP COLUMN username;
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog RENAME COLUMN username_final TO username;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN username_final text;
|
||||
UPDATE @extschema@.cdb_analysis_catalog SET username_final = username;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog DROP COLUMN username;
|
||||
ALTER TABLE @extschema@.cdb_analysis_catalog RENAME COLUMN username_final TO username;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
-- Read configuration parameter analysis_quota_factor, making it
|
||||
-- accessible to regular users (which don't have access to cdb_conf)
|
||||
CREATE OR REPLACE FUNCTION _CDB_GetConfAnalysisQuotaFactor()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GetConfAnalysisQuotaFactor()
|
||||
RETURNS float8 AS
|
||||
$$
|
||||
BEGIN
|
||||
RETURN CDB_Conf_GetConf('analysis_quota_factor')::text::float8;
|
||||
RETURN @extschema@.CDB_Conf_GetConf('analysis_quota_factor')::text::float8;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql' STABLE PARALLEL SAFE SECURITY DEFINER;
|
||||
|
||||
|
||||
-- Get the factor (fraction of the quota) for Camshaft cached analysis tables
|
||||
CREATE OR REPLACE FUNCTION _CDB_AnalysisQuotaFactor()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_AnalysisQuotaFactor()
|
||||
RETURNS float8 AS
|
||||
$$
|
||||
DECLARE
|
||||
factor float8;
|
||||
BEGIN
|
||||
-- We use a floating point cdb_conf parameter
|
||||
factor := _CDB_GetConfAnalysisQuotaFactor();
|
||||
factor := @extschema@._CDB_GetConfAnalysisQuotaFactor();
|
||||
-- With a default value
|
||||
IF factor IS NULL THEN
|
||||
factor := 2;
|
||||
@@ -33,7 +33,7 @@ LANGUAGE 'plpgsql' STABLE PARALLEL SAFE;
|
||||
-- The name of an analysis table is passed; this, in addition to the
|
||||
-- db role that executes this function is used to determined which
|
||||
-- analysis tables will be considered.
|
||||
CREATE OR REPLACE FUNCTION CDB_CheckAnalysisQuota(table_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CheckAnalysisQuota(table_name TEXT)
|
||||
RETURNS void AS
|
||||
$$
|
||||
DECLARE
|
||||
@@ -54,7 +54,7 @@ BEGIN
|
||||
|
||||
SELECT current_schema() INTO schema_name;
|
||||
EXECUTE FORMAT('SELECT %I._CDB_UserQuotaInBytes();', schema_name) INTO nominal_quota;
|
||||
IF nominal_quota*_CDB_AnalysisQuotaFactor() < _CDB_AnalysisDataSize(schema_name) THEN
|
||||
IF nominal_quota * @extschema@._CDB_AnalysisQuotaFactor() < @extschema@._CDB_AnalysisDataSize(schema_name) THEN
|
||||
-- The limit is defined by a factor applied to the total space quota for the user
|
||||
RAISE EXCEPTION 'Analysis cache space limits exceeded';
|
||||
END IF;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
-- This function returns TRUE if a given table name corresponds to a Camshaft cached analysis table
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_IsAnalysisTableName(table_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_IsAnalysisTableName(table_name TEXT)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -15,10 +15,10 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- that may contain user tables are returned.
|
||||
-- For each table, the regclass, schema name and table name are returned.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_AnalysisTablesInSchema(schema_name text DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_AnalysisTablesInSchema(schema_name text DEFAULT NULL)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT * FROM _CDB_UserTablesInSchema(schema_name) WHERE _CDB_IsAnalysisTableName(table_name);
|
||||
SELECT * FROM @extschema@._CDB_UserTablesInSchema(schema_name) WHERE @extschema@._CDB_IsAnalysisTableName(table_name);
|
||||
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
|
||||
-- This function returns a relation user tables excluding analysis tables
|
||||
@@ -26,24 +26,24 @@ $$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
-- that may contain user tables are returned.
|
||||
-- For each table, the regclass, schema name and table name are returned.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_NonAnalysisTablesInSchema(schema_name text DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_NonAnalysisTablesInSchema(schema_name text DEFAULT NULL)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT * FROM _CDB_UserTablesInSchema(schema_name) WHERE Not _CDB_IsAnalysisTableName(table_name);
|
||||
SELECT * FROM @extschema@._CDB_UserTablesInSchema(schema_name) WHERE Not @extschema@._CDB_IsAnalysisTableName(table_name);
|
||||
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
|
||||
-- Total spaced used up by Camshaft cached analysis tables in the given schema.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_AnalysisDataSize(schema_name TEXT DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_AnalysisDataSize(schema_name TEXT DEFAULT NULL)
|
||||
RETURNS bigint AS
|
||||
$$
|
||||
DECLARE
|
||||
total_size bigint;
|
||||
BEGIN
|
||||
WITH analysis_tables AS (
|
||||
SELECT t.schema_name, t.table_name FROM _CDB_AnalysisTablesInSchema(schema_name) t
|
||||
SELECT t.schema_name, t.table_name FROM @extschema@._CDB_AnalysisTablesInSchema(schema_name) t
|
||||
)
|
||||
SELECT COALESCE(INT8(SUM(_CDB_total_relation_size(analysis_tables.schema_name, analysis_tables.table_name))))::int8
|
||||
SELECT COALESCE(INT8(SUM(@extschema@._CDB_total_relation_size(analysis_tables.schema_name, analysis_tables.table_name))))::int8
|
||||
INTO total_size FROM analysis_tables;
|
||||
IF total_size IS NOT NULL THEN
|
||||
RETURN total_size;
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
-- 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)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_check_prerequisites(schema_name TEXT, reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
sql TEXT;
|
||||
BEGIN
|
||||
IF cartodb.schema_exists(schema_name) = false THEN
|
||||
IF @extschema@.schema_exists(schema_name) = false THEN
|
||||
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
|
||||
END IF;
|
||||
|
||||
@@ -29,7 +29,7 @@ END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Drop cartodb triggers (might prevent changing columns)
|
||||
CREATE OR REPLACE FUNCTION _CDB_drop_triggers(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_drop_triggers(reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -53,7 +53,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
-- Cartodb_id creation & validation or renaming if invalid
|
||||
CREATE OR REPLACE FUNCTION _CDB_create_cartodb_id_column(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_create_cartodb_id_column(reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -200,7 +200,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- 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)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_create_triggers(schema_name TEXT, reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -209,28 +209,28 @@ 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()';
|
||||
|| ' FOR EACH STATEMENT EXECUTE PROCEDURE @extschema@.cdb_tablemetadata_trigger()';
|
||||
EXECUTE sql;
|
||||
|
||||
-- "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()';
|
||||
|| ' FOR EACH ROW EXECUTE PROCEDURE @extschema@._CDB_update_the_geom_webmercator()';
|
||||
EXECUTE sql;
|
||||
|
||||
-- "test_quota" and "test_quota_per_row"
|
||||
|
||||
sql := 'CREATE TRIGGER test_quota BEFORE UPDATE OR INSERT ON '
|
||||
|| reloid::text
|
||||
|| ' EXECUTE PROCEDURE public.CDB_CheckQuota(0.1, ''-1'', '''
|
||||
|| ' EXECUTE PROCEDURE @extschema@.CDB_CheckQuota(0.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, ''-1'', '''
|
||||
|| ' FOR EACH ROW EXECUTE PROCEDURE @extschema@.CDB_CheckQuota(0.001, ''-1'', '''
|
||||
|| schema_name::text
|
||||
|| ''')';
|
||||
EXECUTE sql;
|
||||
@@ -239,7 +239,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- 8.b) Create all raster triggers
|
||||
-- NOTE: drop/create has the side-effect of re-enabling disabled triggers
|
||||
CREATE OR REPLACE FUNCTION _CDB_create_raster_triggers(schema_name TEXT, reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_create_raster_triggers(schema_name TEXT, reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -248,21 +248,21 @@ 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()';
|
||||
|| ' FOR EACH STATEMENT EXECUTE PROCEDURE @extschema@.cdb_tablemetadata_trigger()';
|
||||
EXECUTE sql;
|
||||
|
||||
-- "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, ''-1'', '''
|
||||
|| ' EXECUTE PROCEDURE @extschema@.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, ''-1'', '''
|
||||
|| ' FOR EACH ROW EXECUTE PROCEDURE @extschema@.CDB_CheckQuota(0.001, ''-1'', '''
|
||||
|| schema_name::text
|
||||
|| ''')';
|
||||
EXECUTE sql;
|
||||
@@ -272,11 +272,11 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
-- Update the_geom_webmercator
|
||||
CREATE OR REPLACE FUNCTION _CDB_update_the_geom_webmercator()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_update_the_geom_webmercator()
|
||||
RETURNS trigger
|
||||
AS $$
|
||||
BEGIN
|
||||
NEW.the_geom_webmercator := public.CDB_TransformToWebmercator(NEW.the_geom);
|
||||
NEW.the_geom_webmercator := @extschema@.CDB_TransformToWebmercator(NEW.the_geom);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
@@ -284,7 +284,7 @@ $$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
--- Trigger to update the updated_at column. No longer added by default
|
||||
--- but kept here for compatibility with old tables which still have this behavior
|
||||
--- and have it added
|
||||
CREATE OR REPLACE FUNCTION _CDB_update_updated_at()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at := now();
|
||||
@@ -293,7 +293,7 @@ END;
|
||||
$$ LANGUAGE plpgsql VOLATILE;
|
||||
|
||||
-- Auxiliary function
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_is_raster_table(schema_name TEXT, reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_is_raster_table(schema_name TEXT, reloid REGCLASS)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -301,7 +301,7 @@ DECLARE
|
||||
is_raster BOOLEAN;
|
||||
rel_name TEXT;
|
||||
BEGIN
|
||||
IF cartodb.schema_exists(schema_name) = FALSE THEN
|
||||
IF @extschema@.schema_exists(schema_name) = FALSE THEN
|
||||
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
|
||||
END IF;
|
||||
|
||||
@@ -331,11 +331,11 @@ $$ LANGUAGE PLPGSQL STABLE PARALLEL UNSAFE;
|
||||
-- Ensure a table is a "cartodb" table (See https://github.com/CartoDB/cartodb/wiki/CartoDB-user-table)
|
||||
|
||||
DROP FUNCTION IF EXISTS CDB_CartodbfyTable(reloid REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION CDB_CartodbfyTable(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CartodbfyTable(reloid REGCLASS)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN cartodb.CDB_CartodbfyTable('public', reloid);
|
||||
RETURN @extschema@.CDB_CartodbfyTable('public', reloid);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
@@ -388,7 +388,7 @@ $$ LANGUAGE PLPGSQL;
|
||||
-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_Columns(OUT pkey TEXT, OUT geomcol TEXT, OUT mercgeomcol TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Columns(OUT pkey TEXT, OUT geomcol TEXT, OUT mercgeomcol TEXT)
|
||||
RETURNS record
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -401,7 +401,7 @@ END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_Error(message TEXT, funcname TEXT DEFAULT '_CDB_Error')
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Error(message TEXT, funcname TEXT DEFAULT '_CDB_Error')
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -412,7 +412,7 @@ END;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL SAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_SQL(sql TEXT, funcname TEXT DEFAULT '_CDB_SQL')
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_SQL(sql TEXT, funcname TEXT DEFAULT '_CDB_SQL')
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -432,7 +432,7 @@ $$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
-- aware. Find a unique relation name in the given schema, starting from the
|
||||
-- template given. If the template is already unique, just return it;
|
||||
-- otherwise, append an increasing integer until you find a unique variant.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Unique_Relation_Name(schemaname TEXT, relationname TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Relation_Name(schemaname TEXT, relationname TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -451,7 +451,7 @@ $$ LANGUAGE 'plpgsql' VOLATILE PARALLEL SAFE;
|
||||
-- aware. Find a unique column name in the given relation, starting from the
|
||||
-- column name given. If the column name is already unique, just return it;
|
||||
-- otherwise, append an increasing integer until you find a unique variant.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Unique_Column_Name(reloid REGCLASS, columnname TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Column_Name(reloid REGCLASS, columnname TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -471,7 +471,7 @@ $$ LANGUAGE 'plpgsql' VOLATILE PARALLEL SAFE;
|
||||
-- we can no-op on the table copy and just ensure that the
|
||||
-- indexes and triggers are in place
|
||||
DROP FUNCTION IF EXISTS _CDB_Has_Usable_Primary_ID(reloid REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION _CDB_Has_Usable_Primary_ID(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Has_Usable_Primary_ID(reloid REGCLASS)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -485,7 +485,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', 'entered function';
|
||||
|
||||
-- Read in the names of the CartoDB columns
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
-- Do we already have a properly named column?
|
||||
SELECT a.attname, i.indisprimary, i.indisunique, a.attnotnull, a.atttypid
|
||||
@@ -537,8 +537,8 @@ BEGIN
|
||||
|
||||
-- Clean up test constraint
|
||||
IF useable_key THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_pk', reloid::text, const.pkey));
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_integer', reloid::text, const.pkey));
|
||||
PERFORM @extschema@._CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_pk', reloid::text, const.pkey));
|
||||
PERFORM @extschema@._CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_integer', reloid::text, const.pkey));
|
||||
|
||||
-- Move non-valid column out of the way
|
||||
ELSE
|
||||
@@ -546,7 +546,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-valid ''%s''', const.pkey);
|
||||
|
||||
PERFORM _CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, %s', const.pkey));
|
||||
PERFORM @extschema@._CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, %s', const.pkey));
|
||||
|
||||
END IF;
|
||||
|
||||
@@ -569,7 +569,7 @@ BEGIN
|
||||
|
||||
-- Yes! Ok, rename it.
|
||||
IF FOUND THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s RENAME COLUMN %s TO %s', reloid::text, rec.attname, const.pkey),'_CDB_Has_Usable_Primary_ID');
|
||||
PERFORM @extschema@._CDB_SQL(Format('ALTER TABLE %s RENAME COLUMN %s TO %s', reloid::text, rec.attname, const.pkey),'_CDB_Has_Usable_Primary_ID');
|
||||
RETURN true;
|
||||
ELSE
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
@@ -586,7 +586,7 @@ END;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_Has_Usable_PK_Sequence(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Has_Usable_PK_Sequence(reloid REGCLASS)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -595,7 +595,7 @@ DECLARE
|
||||
has_sequence BOOLEAN = false;
|
||||
BEGIN
|
||||
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
SELECT pg_get_serial_sequence(reloid::text, const.pkey)
|
||||
INTO STRICT seq;
|
||||
@@ -607,19 +607,19 @@ $$ LANGUAGE 'plpgsql' STABLE PARALLEL SAFE;
|
||||
|
||||
-- Return a set of columns that can be candidates to be the_geom[webmercator]
|
||||
-- with some extra information to analyze them.
|
||||
CREATE OR REPLACE FUNCTION _cdb_geom_candidate_columns(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_geom_candidate_columns(reloid REGCLASS)
|
||||
RETURNS TABLE (attname name, srid integer, typname name, desired_attname text, desired_srid integer)
|
||||
AS $$
|
||||
DECLARE
|
||||
const RECORD;
|
||||
BEGIN
|
||||
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
a.attname,
|
||||
CASE WHEN t.typname = 'geometry' THEN postgis_typmod_srid(a.atttypmod) ELSE NULL END AS srid,
|
||||
CASE WHEN t.typname = 'geometry' THEN @postgisschema@.postgis_typmod_srid(a.atttypmod) ELSE NULL END AS srid,
|
||||
t.typname,
|
||||
f.desired_attname, f.desired_srid
|
||||
FROM pg_class c
|
||||
@@ -629,15 +629,16 @@ BEGIN
|
||||
WHERE c.oid = reloid
|
||||
AND a.attnum > 0
|
||||
AND NOT a.attisdropped
|
||||
AND postgis_typmod_srid(a.atttypmod) IN (4326, 3857, 0)
|
||||
AND @postgisschema@.postgis_typmod_srid(a.atttypmod) IN (4326, 3857, 0)
|
||||
ORDER BY t.oid ASC;
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' STABLE PARALLEL SAFE;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
SET search_path TO @extschema@;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '_cdb_has_usable_geom_record') THEN
|
||||
CREATE TYPE _cdb_has_usable_geom_record
|
||||
CREATE TYPE @extschema@._cdb_has_usable_geom_record
|
||||
AS (has_usable_geoms boolean,
|
||||
text_geom_column boolean,
|
||||
text_geom_column_name text,
|
||||
@@ -650,8 +651,8 @@ BEGIN
|
||||
END$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS _CDB_Has_Usable_Geom(REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION _CDB_Has_Usable_Geom(reloid REGCLASS)
|
||||
RETURNS _cdb_has_usable_geom_record
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Has_Usable_Geom(reloid REGCLASS)
|
||||
RETURNS @extschema@._cdb_has_usable_geom_record
|
||||
AS $$
|
||||
DECLARE
|
||||
r1 RECORD;
|
||||
@@ -679,11 +680,11 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Geom): %', 'entered function';
|
||||
|
||||
-- Read in the names of the CartoDB columns
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
-- Do we have a column we can use?
|
||||
FOR r1 IN
|
||||
SELECT * FROM _cdb_geom_candidate_columns(reloid)
|
||||
SELECT * FROM @extschema@._cdb_geom_candidate_columns(reloid)
|
||||
LOOP
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Geom): %', Format('checking column ''%s''', r1.attname);
|
||||
@@ -697,7 +698,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Geom): %', Format('column ''%s'' is a text column', r1.attname);
|
||||
|
||||
BEGIN
|
||||
sql := Format('SELECT Max(ST_SRID(%I::geometry)) AS srid FROM %I', r1.attname, reloid::text);
|
||||
sql := Format('SELECT Max(@postgisschema@.ST_SRID(%I::@postgisschema@.geometry)) AS srid FROM %I', r1.attname, reloid::text);
|
||||
EXECUTE sql INTO srid;
|
||||
-- This gets skipped if EXCEPTION happens
|
||||
-- Let the table writer know we need to convert from text
|
||||
@@ -714,9 +715,9 @@ BEGIN
|
||||
WHEN others THEN
|
||||
IF SQLERRM = 'parse error - invalid geometry' THEN
|
||||
text_geom_column := false;
|
||||
str := cartodb._CDB_Unique_Column_Identifier(NULL, r1.attname, NULL, reloid);
|
||||
str := @extschema@._CDB_Unique_Column_Identifier(NULL, r1.attname, NULL, reloid);
|
||||
sql := Format('ALTER TABLE %s RENAME COLUMN %s TO %I', reloid::text, r1.attname, str);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Has_Usable_Geom');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Has_Usable_Geom');
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Geom): %',
|
||||
Format('Text column %s is not convertible to geometry, renamed to %s', r1.attname, str);
|
||||
ELSE
|
||||
@@ -726,9 +727,9 @@ BEGIN
|
||||
|
||||
-- Just change its name so we can write a new column into that name.
|
||||
ELSE
|
||||
str := cartodb._CDB_Unique_Column_Identifier(NULL, r1.attname, NULL, reloid);
|
||||
str := @extschema@._CDB_Unique_Column_Identifier(NULL, r1.attname, NULL, reloid);
|
||||
sql := Format('ALTER TABLE %s RENAME COLUMN %s TO %I', reloid::text, r1.attname, str);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Has_Usable_Geom');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Has_Usable_Geom');
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Geom): %',
|
||||
Format('%s is the wrong type, renamed to %s', r1.attname, str);
|
||||
END IF;
|
||||
@@ -784,7 +785,7 @@ $$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
-- a "good" one, and the same for the geometry columns. If all the required
|
||||
-- columns are in place already, it no-ops and just renames the table to
|
||||
-- the destination if necessary.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Rewrite_Table(reloid REGCLASS, destschema TEXT DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Rewrite_Table(reloid REGCLASS, destschema TEXT DEFAULT NULL)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -820,7 +821,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): %', 'entered function';
|
||||
|
||||
-- Read CartoDB standard column names in
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
-- Save the raw schema/table names for later
|
||||
SELECT n.nspname, c.relname, c.relname
|
||||
@@ -836,7 +837,7 @@ BEGIN
|
||||
-- See if there is a primary key column we need to carry along to the
|
||||
-- new table. If this is true, it implies there is an indexed
|
||||
-- primary key of integer type named (by default) cartodb_id
|
||||
SELECT _CDB_Has_Usable_Primary_ID(reloid)
|
||||
SELECT @extschema@._CDB_Has_Usable_Primary_ID(reloid)
|
||||
INTO STRICT has_usable_primary_key;
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): has_usable_primary_key %', has_usable_primary_key;
|
||||
@@ -854,23 +855,23 @@ BEGIN
|
||||
-- transformation of the table, we can just ensure proper
|
||||
-- indexes are in place and apply a rename
|
||||
SELECT *
|
||||
FROM _CDB_Has_Usable_Geom(reloid)
|
||||
FROM @extschema@._CDB_Has_Usable_Geom(reloid)
|
||||
INTO STRICT gc;
|
||||
|
||||
-- If geom is the wrong name, just rename it.
|
||||
IF gc.has_geom AND gc.has_geom_name != const.geomcol THEN
|
||||
sql := Format('ALTER TABLE %s DROP COLUMN IF EXISTS %I', reloid::text, const.geomcol);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
sql := Format('ALTER TABLE %s RENAME COLUMN %I TO %I', reloid::text, gc.has_geom_name, const.geomcol);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
|
||||
-- If mercgeom is the wrong name, just rename it.
|
||||
IF gc.has_mercgeom AND gc.has_mercgeom_name != const.mercgeomcol THEN
|
||||
sql := Format('ALTER TABLE %s DROP COLUMN IF EXISTS %I', reloid::text, const.mercgeomcol);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
sql := Format('ALTER TABLE %s RENAME COLUMN %I TO %I', reloid::text, gc.has_mercgeom_name, const.mercgeomcol);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
|
||||
|
||||
@@ -885,7 +886,7 @@ BEGIN
|
||||
IF destschema != relschema THEN
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): perfect table needs to be moved to schema (%)', destschema;
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s SET SCHEMA %I', reloid::text, destschema), '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(Format('ALTER TABLE %s SET SCHEMA %I', reloid::text, destschema), '_CDB_Rewrite_Table');
|
||||
|
||||
ELSE
|
||||
|
||||
@@ -911,21 +912,21 @@ BEGIN
|
||||
INTO relseq;
|
||||
-- If it's the name we want, then rename it
|
||||
IF relseq IS NOT NULL AND relseq = Format('%I.%I', destschema, destseq) THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER SEQUENCE %s RENAME TO %I', relseq, Format('tmp_%s', destseq)), '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(Format('ALTER SEQUENCE %s RENAME TO %I', relseq, Format('tmp_%s', destseq)), '_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Put the primary key sequence in the right schema
|
||||
-- If the new table is not moving, better ensure the sequence name
|
||||
-- is unique
|
||||
destseq := cartodb._CDB_Unique_Identifier(NULL, relname, '_' || const.pkey || '_seq', destschema);
|
||||
destseq := @extschema@._CDB_Unique_Identifier(NULL, relname, '_' || const.pkey || '_seq', destschema);
|
||||
destseq := Format('%I.%I', destschema, destseq);
|
||||
PERFORM _CDB_SQL(Format('CREATE SEQUENCE %s', destseq), '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(Format('CREATE SEQUENCE %s', destseq), '_CDB_Rewrite_Table');
|
||||
|
||||
-- Temporary table name if we are re-writing in place
|
||||
-- Note copyname is already escaped and safe to use as identifier
|
||||
IF destschema = relschema THEN
|
||||
copyname := Format('%I.%I', destschema, cartodb._CDB_Unique_Identifier(NULL, destname, NULL), destschema);
|
||||
copyname := Format('%I.%I', destschema, @extschema@._CDB_Unique_Identifier(NULL, destname, NULL), destschema);
|
||||
ELSE
|
||||
copyname := Format('%I.%I', destschema, destname);
|
||||
END IF;
|
||||
@@ -966,11 +967,11 @@ BEGIN
|
||||
ORDER BY a.attnum
|
||||
LIMIT 1
|
||||
)
|
||||
SELECT ', ST_Transform('
|
||||
SELECT ', @postgisschema@.ST_Transform('
|
||||
|| t.missing_srid_start || t.attname || '::geometry' || t.missing_srid_end
|
||||
|| ',4326)::Geometry(GEOMETRY,4326) AS '
|
||||
|| const.geomcol
|
||||
|| ', cartodb.CDB_TransformToWebmercator('
|
||||
|| ', @extschema@.CDB_TransformToWebmercator('
|
||||
|| t.missing_srid_start || t.attname || '::geometry' || t.missing_srid_end
|
||||
|| ')::Geometry(GEOMETRY,3857) AS '
|
||||
|| const.mercgeomcol,
|
||||
@@ -1009,7 +1010,7 @@ BEGIN
|
||||
-- column and read the first SRID off it it, if there is a row
|
||||
-- to read.
|
||||
IF FOUND THEN
|
||||
EXECUTE Format('SELECT ST_SRID(%s) AS srid FROM %s LIMIT 1', rec.attname, reloid::text)
|
||||
EXECUTE Format('SELECT @postgisschema@.ST_SRID(%s) AS srid FROM %s LIMIT 1', rec.attname, reloid::text)
|
||||
INTO geom_srid;
|
||||
ELSE
|
||||
geom_srid := 0;
|
||||
@@ -1025,7 +1026,7 @@ BEGIN
|
||||
SELECT
|
||||
a.attname,
|
||||
postgis_typmod_type(a.atttypmod) AS geomtype,
|
||||
CASE WHEN postgis_typmod_srid(a.atttypmod) = 0 AND srid.srid = 0 THEN 'ST_SetSRID(' ELSE '' END AS missing_srid_start,
|
||||
CASE WHEN postgis_typmod_srid(a.atttypmod) = 0 AND srid.srid = 0 THEN '@postgisschema@.ST_SetSRID(' ELSE '' END AS missing_srid_start,
|
||||
CASE WHEN postgis_typmod_srid(a.atttypmod) = 0 AND srid.srid = 0 THEN ',4326)' ELSE '' END AS missing_srid_end
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
@@ -1038,11 +1039,11 @@ BEGIN
|
||||
ORDER BY a.attnum
|
||||
LIMIT 1
|
||||
)
|
||||
SELECT ', ST_Transform('
|
||||
SELECT ', @postgisschema@.ST_Transform('
|
||||
|| t.missing_srid_start || t.attname || t.missing_srid_end
|
||||
|| ',4326)::Geometry(GEOMETRY,4326) AS '
|
||||
|| const.geomcol
|
||||
|| ', cartodb.CDB_TransformToWebmercator('
|
||||
|| ', @extschema@.CDB_TransformToWebmercator('
|
||||
|| t.missing_srid_start || t.attname || t.missing_srid_end
|
||||
|| ')::Geometry(GEOMETRY,3857) AS '
|
||||
|| const.mercgeomcol,
|
||||
@@ -1070,7 +1071,7 @@ BEGIN
|
||||
-- by selecting their names into an array and
|
||||
-- joining the array with a comma
|
||||
SELECT
|
||||
',' || array_to_string(array_agg(Format('%I',a.attname)),',') AS column_name_sql,
|
||||
',' || array_to_string(array_agg(Format('%I',a.attname) ORDER BY a.attnum),',') AS column_name_sql,
|
||||
Count(*) AS count
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
@@ -1096,7 +1097,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): %', sql;
|
||||
|
||||
-- Run it!
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
|
||||
-- Set up the primary key sequence
|
||||
-- If we copied the primary key from the original data, we need
|
||||
@@ -1106,38 +1107,38 @@ BEGIN
|
||||
INTO destseqmax;
|
||||
|
||||
IF destseqmax IS NOT NULL THEN
|
||||
PERFORM _CDB_SQL(Format('SELECT setval(''%s'', %s)', destseq, destseqmax), '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(Format('SELECT setval(''%s'', %s)', destseq, destseqmax), '_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
|
||||
-- Make the primary key use the sequence as its default value
|
||||
sql := Format('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT nextval(''%s'')',
|
||||
copyname, const.pkey, destseq);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
|
||||
-- Make the sequence owned by the table, so when the table drops,
|
||||
-- the sequence does too
|
||||
sql := Format('ALTER SEQUENCE %s OWNED BY %s.%s', destseq, copyname, const.pkey);
|
||||
PERFORM _CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql,'_CDB_Rewrite_Table');
|
||||
|
||||
|
||||
-- We just made a copy, so we can drop the original now
|
||||
sql := Format('DROP TABLE %s', reloid::text);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
|
||||
-- If the table is being created by a SECURITY DEFINER function
|
||||
-- make sure the user is set back to the user who is connected
|
||||
IF current_user != session_user THEN
|
||||
sql := Format('ALTER TABLE IF EXISTS %s OWNER TO %s', copyname, session_user);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
sql := Format('ALTER SEQUENCE IF EXISTS %s OWNER TO %s', destseq, session_user);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
|
||||
-- If we used a temporary destination table
|
||||
-- we can now rename it into place
|
||||
IF destschema = relschema THEN
|
||||
sql := Format('ALTER TABLE %s RENAME TO %I', copyname, destname);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
END IF;
|
||||
|
||||
RETURN true;
|
||||
@@ -1149,7 +1150,7 @@ $$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
-- Assumes the table already has the right metadata columns
|
||||
-- (primary key and two geometry columns) and adds primary key
|
||||
-- and geometry indexes if necessary.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Add_Indexes(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Add_Indexes(reloid REGCLASS)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -1163,7 +1164,7 @@ BEGIN
|
||||
RAISE DEBUG 'CDB(_CDB_Add_Indexes): %', 'entered function';
|
||||
|
||||
-- Read CartoDB standard column names in
|
||||
const := _CDB_Columns();
|
||||
const := @extschema@._CDB_Columns();
|
||||
|
||||
-- Extract just the relname to use for the index names
|
||||
SELECT c.relname
|
||||
@@ -1189,7 +1190,7 @@ BEGIN
|
||||
IF FOUND THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Add_Indexes): dropping unwanted primary key ''%''', rec.pkey;
|
||||
sql := Format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s', reloid::text, rec.pkey);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
END IF;
|
||||
|
||||
|
||||
@@ -1210,7 +1211,7 @@ BEGIN
|
||||
-- No primary key? Add one.
|
||||
IF NOT FOUND THEN
|
||||
sql := Format('ALTER TABLE %s ADD PRIMARY KEY (%s)', reloid::text, const.pkey);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
END IF;
|
||||
|
||||
-- Add geometry indexes to all "special geometry columns" that
|
||||
@@ -1239,7 +1240,7 @@ BEGIN
|
||||
AND am.amname != 'gist'
|
||||
LOOP
|
||||
sql := Format('CREATE INDEX ON %s USING GIST (%s)', reloid::text, rec.attname);
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
PERFORM @extschema@._CDB_SQL(sql, '_CDB_Add_Indexes');
|
||||
END LOOP;
|
||||
|
||||
RETURN true;
|
||||
@@ -1247,8 +1248,8 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
DROP FUNCTION IF EXISTS CDB_CartodbfyTable(destschema TEXT, reloid REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION CDB_CartodbfyTable(destschema TEXT, reloid REGCLASS)
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_CartodbfyTable(destschema TEXT, reloid REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CartodbfyTable(destschema TEXT, reloid REGCLASS)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -1270,7 +1271,7 @@ BEGIN
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
|
||||
PERFORM cartodb._CDB_check_prerequisites(destschema, reloid);
|
||||
PERFORM @extschema@._CDB_check_prerequisites(destschema, reloid);
|
||||
|
||||
-- Check destination schema exists
|
||||
-- Throws an exception of there is no matching schema
|
||||
@@ -1287,29 +1288,29 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- Drop triggers first
|
||||
PERFORM _CDB_drop_triggers(reloid);
|
||||
PERFORM @extschema@._CDB_drop_triggers(reloid);
|
||||
|
||||
-- Rasters only get a cartodb_id and a limited selection of triggers
|
||||
-- underlying assumption is that they are already formed up correctly
|
||||
SELECT cartodb._CDB_is_raster_table(destschema, reloid) INTO is_raster;
|
||||
SELECT @extschema@._CDB_is_raster_table(destschema, reloid) INTO is_raster;
|
||||
IF is_raster THEN
|
||||
|
||||
PERFORM cartodb._CDB_create_cartodb_id_column(reloid);
|
||||
PERFORM cartodb._CDB_create_raster_triggers(destschema, reloid);
|
||||
PERFORM @extschema@._CDB_create_cartodb_id_column(reloid);
|
||||
PERFORM @extschema@._CDB_create_raster_triggers(destschema, reloid);
|
||||
|
||||
ELSE
|
||||
|
||||
-- Rewrite (or rename) the table to the new location
|
||||
PERFORM _CDB_Rewrite_Table(reloid, destschema);
|
||||
PERFORM @extschema@._CDB_Rewrite_Table(reloid, destschema);
|
||||
|
||||
-- The old regclass might not be valid anymore if we re-wrote the table...
|
||||
destoid := (destschema || '.' || destname)::regclass;
|
||||
|
||||
-- Add indexes to the destination table, as necessary
|
||||
PERFORM _CDB_Add_Indexes(destoid);
|
||||
PERFORM @extschema@._CDB_Add_Indexes(destoid);
|
||||
|
||||
-- Add triggers to the destination table, as necessary
|
||||
PERFORM _CDB_create_triggers(destschema, destoid);
|
||||
PERFORM @extschema@._CDB_create_triggers(destschema, destoid);
|
||||
|
||||
END IF;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Function returning the column names of a table
|
||||
CREATE OR REPLACE FUNCTION CDB_ColumnNames(REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_ColumnNames(REGCLASS)
|
||||
RETURNS SETOF information_schema.sql_identifier
|
||||
AS $$
|
||||
SELECT
|
||||
@@ -13,4 +13,4 @@ $$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
|
||||
GRANT EXECUTE ON FUNCTION CDB_ColumnNames(REGCLASS) TO PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION @extschema@.CDB_ColumnNames(REGCLASS) TO PUBLIC;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Function returning the type of a column
|
||||
CREATE OR REPLACE FUNCTION CDB_ColumnType(REGCLASS, TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_ColumnType(REGCLASS, TEXT)
|
||||
RETURNS information_schema.character_data
|
||||
AS $$
|
||||
SELECT
|
||||
@@ -13,4 +13,4 @@ $$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
|
||||
GRANT EXECUTE ON FUNCTION CDB_ColumnType(REGCLASS, TEXT) TO public;
|
||||
GRANT EXECUTE ON FUNCTION @extschema@.CDB_ColumnType(REGCLASS, TEXT) TO public;
|
||||
|
||||
@@ -5,44 +5,44 @@
|
||||
-- Functions needing reading configuration should use SECURITY DEFINER.
|
||||
----------------------------------
|
||||
|
||||
-- This will trigger NOTICE if cartodb.CDB_CONF already exists
|
||||
-- This will trigger NOTICE if @extschema@.CDB_CONF already exists
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
CREATE TABLE IF NOT EXISTS cartodb.CDB_CONF ( KEY TEXT PRIMARY KEY, VALUE JSON NOT NULL );
|
||||
CREATE TABLE IF NOT EXISTS @extschema@.CDB_CONF ( KEY TEXT PRIMARY KEY, VALUE JSON NOT NULL );
|
||||
END
|
||||
$$;
|
||||
|
||||
-- This can only be called from an SQL script executed by CREATE EXTENSION
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
PERFORM pg_catalog.pg_extension_config_dump('cartodb.CDB_CONF', '');
|
||||
PERFORM pg_catalog.pg_extension_config_dump('@extschema@.CDB_CONF', '');
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Conf_SetConf(key text, value JSON)
|
||||
FUNCTION @extschema@.CDB_Conf_SetConf(key text, value JSON)
|
||||
RETURNS void AS $$
|
||||
BEGIN
|
||||
PERFORM cartodb.CDB_Conf_RemoveConf(key);
|
||||
EXECUTE 'INSERT INTO cartodb.CDB_CONF (KEY, VALUE) VALUES ($1, $2);' USING key, value;
|
||||
PERFORM @extschema@.CDB_Conf_RemoveConf(key);
|
||||
EXECUTE 'INSERT INTO @extschema@.CDB_CONF (KEY, VALUE) VALUES ($1, $2);' USING key, value;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Conf_RemoveConf(key text)
|
||||
FUNCTION @extschema@.CDB_Conf_RemoveConf(key text)
|
||||
RETURNS void AS $$
|
||||
BEGIN
|
||||
EXECUTE 'DELETE FROM cartodb.CDB_CONF WHERE KEY = $1;' USING key;
|
||||
EXECUTE 'DELETE FROM @extschema@.CDB_CONF WHERE KEY = $1;' USING key;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Conf_GetConf(key text)
|
||||
FUNCTION @extschema@.CDB_Conf_GetConf(key text)
|
||||
RETURNS JSON AS $$
|
||||
DECLARE
|
||||
value JSON;
|
||||
BEGIN
|
||||
EXECUTE 'SELECT VALUE FROM cartodb.CDB_CONF WHERE KEY = $1;' INTO value USING key;
|
||||
EXECUTE 'SELECT VALUE FROM @extschema@.CDB_CONF WHERE KEY = $1;' INTO value USING key;
|
||||
RETURN value;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
|
||||
|
||||
14
scripts-available/CDB_DDLTriggers.sql
Normal file
14
scripts-available/CDB_DDLTriggers.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
--
|
||||
-- Legacy file
|
||||
-- Introduced again to make sure that updates do not leave dangling functions
|
||||
--
|
||||
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_create_table();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_drop_table();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_alter_column();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_drop_column();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_add_column();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_disable_ddl_hooks();
|
||||
DROP FUNCTION IF EXISTS @extschema@.cdb_enable_ddl_hooks();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- Convert timestamp to double precision
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_DateToNumber(input timestamp)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DateToNumber(input timestamp)
|
||||
RETURNS double precision AS $$
|
||||
DECLARE output double precision;
|
||||
BEGIN
|
||||
@@ -16,7 +16,7 @@ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL UNSAFE;
|
||||
|
||||
-- Convert timestamp with time zone to double precision
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_DateToNumber(input timestamp with time zone)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DateToNumber(input timestamp with time zone)
|
||||
RETURNS double precision AS $$
|
||||
DECLARE output double precision;
|
||||
BEGIN
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Find thousand and decimal digits separators
|
||||
CREATE OR REPLACE FUNCTION CDB_DigitSeparator (rel REGCLASS, fld TEXT, OUT t CHAR, OUT d CHAR)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DigitSeparator (rel REGCLASS, fld TEXT, OUT t CHAR, OUT d CHAR)
|
||||
as $$
|
||||
DECLARE
|
||||
sql TEXT;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
-- 1. width_bucket/histograms: http://tapoueh.org/blog/2014/02/21-PostgreSQL-histogram
|
||||
-- 2. R implementation: https://github.com/cran/agrmt
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_DistType ( in_array NUMERIC[] ) RETURNS text as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DistType ( in_array NUMERIC[] ) RETURNS text as $$
|
||||
DECLARE
|
||||
element_count INT4;
|
||||
minv numeric;
|
||||
@@ -60,7 +60,7 @@ BEGIN
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
signature = _CDB_DistTypeClassify(ajus);
|
||||
signature = @extschema@._CDB_DistTypeClassify(ajus);
|
||||
END IF;
|
||||
|
||||
RETURN signature;
|
||||
@@ -69,7 +69,7 @@ $$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- Classify data into AJUSFL
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_DistTypeClassify ( in_array INT[] ) RETURNS text as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_DistTypeClassify ( in_array INT[] ) RETURNS text as $$
|
||||
DECLARE
|
||||
element_count INT4;
|
||||
maxv numeric;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_DistinctMeasure ( in_array text[], threshold numeric DEFAULT null ) RETURNS numeric as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DistinctMeasure ( in_array text[], threshold numeric DEFAULT null ) RETURNS numeric as $$
|
||||
DECLARE
|
||||
element_count INT4;
|
||||
maxval numeric;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_EqualIntervalBins ( in_array anyarray, breaks INT ) RETURNS anyarray as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_EqualIntervalBins ( in_array anyarray, breaks INT ) RETURNS anyarray as $$
|
||||
WITH stats AS (
|
||||
SELECT min(e), (max(e)-min(e))/breaks AS del
|
||||
FROM (SELECT unnest(in_array) e) AS p)
|
||||
@@ -21,4 +21,4 @@ SELECT array_agg(bins)
|
||||
FROM stats) q;
|
||||
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
DROP FUNCTION IF EXISTS CDB_EqualIntervalBins( numeric[], integer);
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_EqualIntervalBins( numeric[], integer);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Internal function to generate stats for a table if they don't exist
|
||||
CREATE OR REPLACE FUNCTION _CDB_GenerateStats(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GenerateStats(reloid REGCLASS)
|
||||
RETURNS VOID
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -12,20 +12,20 @@ BEGIN
|
||||
EXECUTE Format('ANALYZE %s;', reloid);
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL RESTRICTED SECURITY DEFINER;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
-- Return a row count estimate of the result of a query using statistics
|
||||
CREATE OR REPLACE FUNCTION CDB_EstimateRowCount(query text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_EstimateRowCount(query text)
|
||||
RETURNS Numeric
|
||||
AS $$
|
||||
DECLARE
|
||||
plan JSON;
|
||||
BEGIN
|
||||
-- Make sure statistics exist for all the tables of the query
|
||||
PERFORM _CDB_GenerateStats(tabname) FROM unnest(CDB_QueryTablesText(query)) AS tabname;
|
||||
PERFORM @extschema@._CDB_GenerateStats(tabname) FROM unnest(@extschema@.CDB_QueryTablesText(query)) AS tabname;
|
||||
|
||||
-- Use the query planner to obtain an estimate of the number of result rows
|
||||
EXECUTE 'EXPLAIN (FORMAT JSON) ' || query INTO STRICT plan;
|
||||
RETURN plan->0->'Plan'->'Plan Rows';
|
||||
END
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL RESTRICTED;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
SELECT pg_catalog.pg_extension_config_dump('cartodb.cdb_tablemetadata','');
|
||||
SELECT pg_catalog.pg_extension_config_dump('@extschema@.cdb_tablemetadata','');
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
CREATE OR REPLACE FUNCTION cartodb.cdb_extension_reload() RETURNS void
|
||||
CREATE OR REPLACE FUNCTION @extschema@.cdb_extension_reload() RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
ver TEXT;
|
||||
sql TEXT;
|
||||
BEGIN
|
||||
ver := split_part(cartodb.cdb_version(), ' ', 1);
|
||||
ver := split_part(@extschema@.cdb_version(), ' ', 1);
|
||||
sql := 'ALTER EXTENSION cartodb UPDATE TO ''' || ver || 'next''';
|
||||
EXECUTE sql;
|
||||
sql := 'ALTER EXTENSION cartodb UPDATE TO ''' || ver || '''';
|
||||
@@ -12,7 +12,7 @@ BEGIN
|
||||
END;
|
||||
$$ language 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.schema_exists(schema_name text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.schema_exists(schema_name text)
|
||||
RETURNS boolean AS
|
||||
$$
|
||||
SELECT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = schema_name::text);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-- All the FDW settings are read from the `cdb_conf.fdws` entry json file.
|
||||
---------------------------
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Setup_FDW(fdw_name text, config json)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDW(fdw_name text, config json)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -58,12 +58,12 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- Give the organization role usage permisions over the schema
|
||||
SELECT cartodb.CDB_Organization_Member_Group_Role_Member_Name() INTO org_role;
|
||||
SELECT @extschema@.CDB_Organization_Member_Group_Role_Member_Name() INTO org_role;
|
||||
EXECUTE FORMAT ('GRANT USAGE ON SCHEMA %I TO %I', fdw_name, org_role);
|
||||
|
||||
-- Bring here the remote cdb_tablemetadata
|
||||
IF NOT EXISTS ( SELECT * FROM PG_CLASS WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname=fdw_name) and relname='cdb_tablemetadata') THEN
|
||||
EXECUTE FORMAT ('CREATE FOREIGN TABLE %I.cdb_tablemetadata (tabname text, updated_at timestamp with time zone) SERVER %I OPTIONS (table_name ''cdb_tablemetadata_text'', schema_name ''public'', updatable ''false'')', fdw_name, fdw_name);
|
||||
EXECUTE FORMAT ('CREATE FOREIGN TABLE %I.cdb_tablemetadata (tabname text, updated_at timestamp with time zone) SERVER %I OPTIONS (table_name ''cdb_tablemetadata_text'', schema_name ''@extschema@'', updatable ''false'')', fdw_name, fdw_name);
|
||||
END IF;
|
||||
EXECUTE FORMAT ('GRANT SELECT ON %I.cdb_tablemetadata TO %I', fdw_name, org_role);
|
||||
|
||||
@@ -71,37 +71,37 @@ END
|
||||
$$
|
||||
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Setup_FDWS()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDWS()
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
DECLARE
|
||||
row record;
|
||||
BEGIN
|
||||
FOR row IN SELECT p.key, p.value from lateral json_each(cartodb.CDB_Conf_GetConf('fdws')) p LOOP
|
||||
EXECUTE 'SELECT cartodb._CDB_Setup_FDW($1, $2)' USING row.key, row.value;
|
||||
FOR row IN SELECT p.key, p.value from lateral json_each(@extschema@.CDB_Conf_GetConf('fdws')) p LOOP
|
||||
EXECUTE 'SELECT @extschema@._CDB_Setup_FDW($1, $2)' USING row.key, row.value;
|
||||
END LOOP;
|
||||
END
|
||||
$$
|
||||
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Setup_FDW(fdw_name text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDW(fdw_name text)
|
||||
RETURNS void AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
config json;
|
||||
BEGIN
|
||||
SELECT p.value FROM LATERAL json_each(cartodb.CDB_Conf_GetConf('fdws')) p WHERE p.key = fdw_name INTO config;
|
||||
EXECUTE 'SELECT cartodb._CDB_Setup_FDW($1, $2)' USING fdw_name, config;
|
||||
SELECT p.value FROM LATERAL json_each(@extschema@.CDB_Conf_GetConf('fdws')) p WHERE p.key = fdw_name INTO config;
|
||||
EXECUTE 'SELECT @extschema@._CDB_Setup_FDW($1, $2)' USING fdw_name, config;
|
||||
END
|
||||
$BODY$
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_Add_Remote_Table(source text, table_name text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Add_Remote_Table(source text, table_name text)
|
||||
RETURNS void AS
|
||||
$$
|
||||
BEGIN
|
||||
PERFORM cartodb._CDB_Setup_FDW(source);
|
||||
PERFORM @extschema@._CDB_Setup_FDW(source);
|
||||
EXECUTE FORMAT ('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I;', source, table_name, source, source);
|
||||
--- Grant SELECT to publicuser
|
||||
EXECUTE FORMAT ('GRANT SELECT ON %I.%I TO publicuser;', source, table_name);
|
||||
@@ -109,7 +109,7 @@ END
|
||||
$$
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_Get_Foreign_Updated_At(foreign_table regclass)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Get_Foreign_Updated_At(foreign_table regclass)
|
||||
RETURNS timestamp with time zone AS
|
||||
$$
|
||||
DECLARE
|
||||
@@ -125,14 +125,191 @@ BEGIN
|
||||
|
||||
-- We assume that the remote cdb_tablemetadata is called cdb_tablemetadata and is on the same schema as the queried table.
|
||||
SELECT nspname FROM pg_class c, pg_namespace n WHERE c.oid=foreign_table AND c.relnamespace = n.oid INTO fdw_schema_name;
|
||||
EXECUTE FORMAT('SELECT updated_at FROM %I.cdb_tablemetadata WHERE tabname=%L ORDER BY updated_at DESC LIMIT 1', fdw_schema_name, remote_table_name) INTO time;
|
||||
BEGIN
|
||||
EXECUTE FORMAT('SELECT updated_at FROM %I.cdb_tablemetadata WHERE tabname=%L ORDER BY updated_at DESC LIMIT 1', fdw_schema_name, remote_table_name) INTO time;
|
||||
EXCEPTION
|
||||
WHEN undefined_table THEN
|
||||
-- If you add a GET STACKED DIAGNOSTICS text_var = RETURNED_SQLSTATE
|
||||
-- you get a code 42P01 which corresponds to undefined_table
|
||||
RAISE NOTICE 'CDB_Get_Foreign_Updated_At: could not find %.cdb_tablemetadata while checking % updated_at, returning NULL timestamp', fdw_schema_name, foreign_table;
|
||||
END;
|
||||
RETURN time;
|
||||
END
|
||||
$$
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._cdb_dbname_of_foreign_table(reloid oid)
|
||||
-- Produce a valid DB name for objects created for the user FDW's
|
||||
CREATE OR REPLACE FUNCTION @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name NAME)
|
||||
RETURNS NAME AS $$
|
||||
-- Note on input we use %s and on output we use %I, in order to
|
||||
-- avoid double escaping
|
||||
SELECT format('cdb_fdw_%s', fdw_input_name)::NAME;
|
||||
$$
|
||||
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- A function to set up a user-defined foreign data server
|
||||
-- It does not read from CDB_Conf.
|
||||
-- Only superuser roles can invoke it successfully
|
||||
--
|
||||
-- Sample call:
|
||||
-- SELECT cartodb.CDB_SetUp_User_PG_FDW_Server('amazon', '{
|
||||
-- "server": {
|
||||
-- "extensions": "postgis",
|
||||
-- "dbname": "testdb",
|
||||
-- "host": "myhostname.us-east-2.rds.amazonaws.com",
|
||||
-- "port": "5432"
|
||||
-- },
|
||||
-- "user_mapping": {
|
||||
-- "user": "fdw_user",
|
||||
-- "password": "secret"
|
||||
-- }
|
||||
-- }');
|
||||
--
|
||||
-- Underneath it will:
|
||||
-- * Set up postgresql_fdw
|
||||
-- * Create a server with the name 'cdb_fdw_amazon'
|
||||
-- * Create a role called 'cdb_fdw_amazon' to manage access
|
||||
-- * Create a user mapping with that role 'cdb_fdw_amazon'
|
||||
-- * Create a schema 'cdb_fdw_amazon' as a convenience to set up all foreign
|
||||
-- tables over there
|
||||
--
|
||||
-- It is the responsibility of the superuser to grant that role to either:
|
||||
-- * Nobody
|
||||
-- * Specific roles: GRANT amazon TO role_name;
|
||||
-- * Members of the organization: SELECT cartodb.CDB_Organization_Grant_Role('cdb_fdw_amazon');
|
||||
-- * The publicuser: GRANT cdb_fdw_amazon TO publicuser;
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_SetUp_User_PG_FDW_Server(fdw_input_name NAME, config json)
|
||||
RETURNS void AS $$
|
||||
DECLARE
|
||||
row record;
|
||||
option record;
|
||||
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||
BEGIN
|
||||
-- TODO: refactor with original function
|
||||
-- This function tries to be as idempotent as possible, by not creating anything more than once
|
||||
-- (not even using IF NOT EXIST to avoid throwing warnings)
|
||||
IF NOT EXISTS ( SELECT * FROM pg_extension WHERE extname = 'postgres_fdw') THEN
|
||||
CREATE EXTENSION postgres_fdw;
|
||||
RAISE NOTICE 'Created postgres_fdw extension';
|
||||
END IF;
|
||||
-- Create FDW first if it does not exist
|
||||
IF NOT EXISTS ( SELECT * FROM pg_foreign_server WHERE srvname = fdw_objects_name)
|
||||
THEN
|
||||
EXECUTE FORMAT('CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw', fdw_objects_name);
|
||||
RAISE NOTICE 'Created server % using postgres_fdw', fdw_objects_name;
|
||||
END IF;
|
||||
|
||||
-- Set FDW settings
|
||||
FOR row IN SELECT p.key, p.value from lateral json_each_text(config->'server') p
|
||||
LOOP
|
||||
IF NOT EXISTS (WITH a AS (select split_part(unnest(srvoptions), '=', 1) as options from pg_foreign_server where srvname=fdw_objects_name) SELECT * from a where options = row.key)
|
||||
THEN
|
||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (ADD %I %L)', fdw_objects_name, row.key, row.value);
|
||||
ELSE
|
||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (SET %I %L)', fdw_objects_name, row.key, row.value);
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
-- Create specific role for this
|
||||
IF NOT EXISTS ( SELECT 1 FROM pg_roles WHERE rolname = fdw_objects_name) THEN
|
||||
EXECUTE format('CREATE ROLE %I NOLOGIN', fdw_objects_name);
|
||||
RAISE NOTICE 'Created special role % to access the correponding FDW', fdw_objects_name;
|
||||
END IF;
|
||||
|
||||
-- Transfer ownership of the server to the fdw role
|
||||
EXECUTE format('ALTER SERVER %I OWNER TO %I', fdw_objects_name, fdw_objects_name);
|
||||
|
||||
-- Create user mapping
|
||||
-- NOTE: we use a PUBLIC user mapping but control access to the SERVER
|
||||
-- so that we don't need to create a mapping for every user nor store credentials elsewhere
|
||||
IF NOT EXISTS ( SELECT * FROM pg_user_mappings WHERE srvname = fdw_objects_name AND usename = 'public' ) THEN
|
||||
EXECUTE FORMAT ('CREATE USER MAPPING FOR public SERVER %I', fdw_objects_name);
|
||||
RAISE NOTICE 'Created user mapping for accesing foreign server %', fdw_objects_name;
|
||||
END IF;
|
||||
|
||||
-- Update user mapping settings
|
||||
FOR option IN SELECT o.key, o.value from lateral json_each_text(config->'user_mapping') o LOOP
|
||||
IF NOT EXISTS (WITH a AS (select split_part(unnest(umoptions), '=', 1) as options from pg_user_mappings WHERE srvname = fdw_objects_name AND usename = 'public') SELECT * from a where options = option.key) THEN
|
||||
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (ADD %I %L)', fdw_objects_name, option.key, option.value);
|
||||
ELSE
|
||||
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (SET %I %L)', fdw_objects_name, option.key, option.value);
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
-- Grant usage on the wrapper and server to the fdw role
|
||||
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO %I', fdw_objects_name);
|
||||
RAISE NOTICE 'Granted usage on the postgres_fdw to the role %', fdw_objects_name;
|
||||
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN SERVER %I TO %I', fdw_objects_name, fdw_objects_name);
|
||||
RAISE NOTICE 'Granted usage on the foreign server to the role %', fdw_objects_name;
|
||||
|
||||
-- Create schema if it does not exist.
|
||||
IF NOT EXISTS ( SELECT * from pg_namespace WHERE nspname=fdw_objects_name) THEN
|
||||
EXECUTE FORMAT ('CREATE SCHEMA %I', fdw_objects_name);
|
||||
RAISE NOTICE 'Created schema % to host foreign tables', fdw_objects_name;
|
||||
END IF;
|
||||
|
||||
-- Give the fdw role ownership over the schema
|
||||
EXECUTE FORMAT ('ALTER SCHEMA %I OWNER TO %I', fdw_objects_name, fdw_objects_name);
|
||||
RAISE NOTICE 'Gave ownership on the schema % to %', fdw_objects_name, fdw_objects_name;
|
||||
|
||||
-- TODO: Bring here the remote cdb_tablemetadata
|
||||
END
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
-- A function to drop a user-defined foreign server and all related objects
|
||||
-- It does not read from CDB_Conf
|
||||
-- It must be executed with a superuser role to succeed
|
||||
--
|
||||
-- Sample call:
|
||||
-- SELECT cartodb.CDB_Drop_User_PG_FDW_Server('amazon')
|
||||
--
|
||||
-- Note: if there's any dependent object (i.e. foreign table) this call will fail
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Drop_User_PG_FDW_Server(fdw_input_name NAME, force boolean = false)
|
||||
RETURNS void AS $$
|
||||
DECLARE
|
||||
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||
cascade_clause NAME;
|
||||
BEGIN
|
||||
CASE force
|
||||
WHEN true THEN
|
||||
cascade_clause := 'CASCADE';
|
||||
ELSE
|
||||
cascade_clause := 'RESTRICT';
|
||||
END CASE;
|
||||
|
||||
EXECUTE FORMAT ('DROP SCHEMA %I %s', fdw_objects_name, cascade_clause);
|
||||
RAISE NOTICE 'Dropped schema %', fdw_objects_name;
|
||||
EXECUTE FORMAT ('DROP USER MAPPING FOR public SERVER %I', fdw_objects_name);
|
||||
RAISE NOTICE 'Dropped user mapping for server %', fdw_objects_name;
|
||||
EXECUTE FORMAT ('DROP SERVER %I %s', fdw_objects_name, cascade_clause);
|
||||
RAISE NOTICE 'Dropped foreign server %', fdw_objects_name;
|
||||
EXECUTE FORMAT ('REVOKE USAGE ON FOREIGN DATA WRAPPER postgres_fdw FROM %I %s', fdw_objects_name, cascade_clause);
|
||||
RAISE NOTICE 'Revoked usage on postgres_fdw from %', fdw_objects_name;
|
||||
EXECUTE FORMAT ('DROP ROLE %I', fdw_objects_name);
|
||||
RAISE NOTICE 'Dropped role %', fdw_objects_name;
|
||||
END
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
-- Set up a user foreign table
|
||||
-- E.g:
|
||||
-- SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('amazon', 'carto_lite', 'mytable');
|
||||
-- SELECT * FROM amazon.my_table;
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUp_User_PG_FDW_Table(fdw_input_name NAME, foreign_schema NAME, table_name NAME)
|
||||
RETURNS void AS $$
|
||||
DECLARE
|
||||
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||
BEGIN
|
||||
EXECUTE FORMAT ('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I;', foreign_schema, table_name, fdw_objects_name, fdw_objects_name);
|
||||
--- Grant SELECT to fdw role
|
||||
EXECUTE FORMAT ('GRANT SELECT ON %I.%I TO %I;', fdw_objects_name, table_name, fdw_objects_name);
|
||||
END
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_dbname_of_foreign_table(reloid oid)
|
||||
RETURNS TEXT AS $$
|
||||
SELECT option_value FROM pg_options_to_table((
|
||||
|
||||
@@ -149,18 +326,18 @@ $$ LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
|
||||
-- It is aware of foreign tables
|
||||
-- It assumes the local (schema_name, table_name) map to the remote ones with the same name
|
||||
-- Note: dbname is never quoted whereas schema and table names are when needed.
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_QueryTables_Updated_At(query text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryTables_Updated_At(query text)
|
||||
RETURNS TABLE(dbname text, schema_name text, table_name text, updated_at timestamptz)
|
||||
AS $$
|
||||
WITH query_tables AS (
|
||||
SELECT unnest(CDB_QueryTablesText(query)) schema_table_name
|
||||
SELECT unnest(@extschema@.CDB_QueryTablesText(query)) schema_table_name
|
||||
), query_tables_oid AS (
|
||||
SELECT schema_table_name, schema_table_name::regclass::oid AS reloid
|
||||
FROM query_tables
|
||||
),
|
||||
fqtn AS (
|
||||
SELECT
|
||||
(CASE WHEN c.relkind = 'f' THEN cartodb._cdb_dbname_of_foreign_table(query_tables_oid.reloid)
|
||||
(CASE WHEN c.relkind = 'f' THEN @extschema@._cdb_dbname_of_foreign_table(query_tables_oid.reloid)
|
||||
ELSE current_database()
|
||||
END)::text AS dbname,
|
||||
quote_ident(n.nspname::text) schema_name,
|
||||
@@ -172,8 +349,8 @@ AS $$
|
||||
WHERE c.oid = query_tables_oid.reloid
|
||||
)
|
||||
SELECT fqtn.dbname, fqtn.schema_name, fqtn.table_name,
|
||||
(CASE WHEN relkind = 'f' THEN cartodb.CDB_Get_Foreign_Updated_At(reloid)
|
||||
ELSE (SELECT md.updated_at FROM CDB_TableMetadata md WHERE md.tabname = reloid)
|
||||
(CASE WHEN relkind = 'f' THEN @extschema@.CDB_Get_Foreign_Updated_At(reloid)
|
||||
ELSE (SELECT md.updated_at FROM @extschema@.CDB_TableMetadata md WHERE md.tabname = reloid)
|
||||
END) AS updated_at
|
||||
FROM fqtn;
|
||||
$$ LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
|
||||
@@ -182,7 +359,7 @@ $$ LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Return the last updated time of a set of tables
|
||||
-- It is aware of foreign tables
|
||||
-- It assumes the local (schema_name, table_name) map to the remote ones with the same name
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_Last_Updated_Time(tables text[])
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Last_Updated_Time(tables text[])
|
||||
RETURNS timestamptz AS $$
|
||||
WITH t AS (
|
||||
SELECT unnest(tables) AS schema_table_name
|
||||
@@ -190,8 +367,8 @@ RETURNS timestamptz AS $$
|
||||
SELECT (t.schema_table_name)::regclass::oid as reloid FROM t
|
||||
), t_updated_at AS (
|
||||
SELECT
|
||||
(CASE WHEN relkind = 'f' THEN cartodb.CDB_Get_Foreign_Updated_At(reloid)
|
||||
ELSE (SELECT md.updated_at FROM CDB_TableMetadata md WHERE md.tabname = reloid)
|
||||
(CASE WHEN relkind = 'f' THEN @extschema@.CDB_Get_Foreign_Updated_At(reloid)
|
||||
ELSE (SELECT md.updated_at FROM @extschema@.CDB_TableMetadata md WHERE md.tabname = reloid)
|
||||
END) AS updated_at
|
||||
FROM t_oid
|
||||
LEFT JOIN pg_catalog.pg_class c ON c.oid = reloid
|
||||
|
||||
123
scripts-available/CDB_GhostTables.sql
Normal file
123
scripts-available/CDB_GhostTables.sql
Normal file
@@ -0,0 +1,123 @@
|
||||
-- Enqueues a job to run Ghost tables linking process for the provided username
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_LinkGhostTables(username text, db_name text, event_name text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
if not username:
|
||||
return
|
||||
|
||||
if 'json' not in GD:
|
||||
import json
|
||||
GD['json'] = json
|
||||
else:
|
||||
json = GD['json']
|
||||
|
||||
tis_config = plpy.execute("select @extschema@.CDB_Conf_GetConf('invalidation_service');")[0]['cdb_conf_getconf']
|
||||
if not tis_config:
|
||||
plpy.warning('Invalidation service configuration not found. Skipping Ghost Tables linking.')
|
||||
return
|
||||
|
||||
tis_config_dict = json.loads(tis_config)
|
||||
tis_host = tis_config_dict.get('host')
|
||||
tis_port = tis_config_dict.get('port')
|
||||
tis_timeout = tis_config_dict.get('timeout', 5)
|
||||
tis_retry = tis_config_dict.get('retry', 5)
|
||||
|
||||
client = GD.get('invalidation', None)
|
||||
|
||||
while True:
|
||||
|
||||
if not client:
|
||||
try:
|
||||
import redis
|
||||
client = redis.Redis(host=tis_host, port=tis_port, socket_timeout=tis_timeout)
|
||||
GD['invalidation'] = client
|
||||
except Exception as err:
|
||||
error = "client_error - %s" % str(err)
|
||||
# NOTE: no retries on connection error
|
||||
plpy.warning('Error trying to connect to Invalidation Service to link Ghost Tables: ' + str(err))
|
||||
break
|
||||
|
||||
try:
|
||||
client.execute_command('DBSCH', db_name, username, event_name)
|
||||
break
|
||||
except Exception as err:
|
||||
error = "request_error - %s" % str(err)
|
||||
client = GD['invalidation'] = None # force reconnect
|
||||
if not tis_retry:
|
||||
plpy.warning('Error calling Invalidation Service to link Ghost Tables: ' + str(err))
|
||||
break
|
||||
tis_retry -= 1 # try reconnecting
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Enqueues a job to run Ghost tables linking process for the current user
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_LinkGhostTables(event_name text DEFAULT 'USER')
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
username TEXT;
|
||||
db_name TEXT;
|
||||
BEGIN
|
||||
EXECUTE 'SELECT @extschema@.CDB_Username();' INTO username;
|
||||
EXECUTE 'SELECT current_database();' INTO db_name;
|
||||
|
||||
PERFORM @extschema@._CDB_LinkGhostTables(username, db_name, event_name);
|
||||
RAISE NOTICE '_CDB_LinkGhostTables() called with username=%, event_name=%', username, event_name;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
-- Trigger function to call CDB_LinkGhostTables()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_LinkGhostTablesTrigger()
|
||||
RETURNS trigger
|
||||
AS $$
|
||||
DECLARE
|
||||
ddl_tag TEXT;
|
||||
BEGIN
|
||||
EXECUTE 'DELETE FROM @extschema@.cdb_ddl_execution WHERE txid = txid_current() RETURNING tag;' INTO ddl_tag;
|
||||
PERFORM @extschema@.CDB_LinkGhostTables(ddl_tag);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
-- Event trigger to save the current transaction in @extschema@.cdb_ddl_execution
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SaveDDLTransaction()
|
||||
RETURNS event_trigger
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO @extschema@.cdb_ddl_execution VALUES (txid_current(), tg_tag) ON CONFLICT ON CONSTRAINT cdb_ddl_execution_pkey DO NOTHING;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
-- Creates the trigger on DDL events to link ghost tables
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_EnableGhostTablesTrigger()
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
DROP EVENT TRIGGER IF EXISTS link_ghost_tables;
|
||||
DROP TRIGGER IF EXISTS check_ddl_update ON @extschema@.cdb_ddl_execution;
|
||||
|
||||
-- Table to store the transaction id from DDL events to avoid multiple executions
|
||||
CREATE TABLE IF NOT EXISTS @extschema@.cdb_ddl_execution(txid bigint PRIMARY KEY, tag text);
|
||||
|
||||
CREATE CONSTRAINT TRIGGER check_ddl_update
|
||||
AFTER INSERT ON @extschema@.cdb_ddl_execution
|
||||
INITIALLY DEFERRED
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE @extschema@._CDB_LinkGhostTablesTrigger();
|
||||
|
||||
CREATE EVENT TRIGGER link_ghost_tables
|
||||
ON ddl_command_end
|
||||
WHEN TAG IN ('CREATE TABLE', 'SELECT INTO', 'DROP TABLE', 'ALTER TABLE', 'CREATE TRIGGER', 'DROP TRIGGER', 'CREATE VIEW', 'DROP VIEW', 'ALTER VIEW', 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE', 'DROP FOREIGN TABLE')
|
||||
EXECUTE PROCEDURE @extschema@.CDB_SaveDDLTransaction();
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Drops the trigger on DDL events to link ghost tables
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DisableGhostTablesTrigger()
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
DROP EVENT TRIGGER IF EXISTS link_ghost_tables;
|
||||
DROP TRIGGER IF EXISTS check_ddl_update ON @extschema@.cdb_ddl_execution;
|
||||
DROP TABLE IF EXISTS @extschema@.cdb_ddl_execution;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
@@ -1,23 +1,23 @@
|
||||
-- Great circle point-to-point routes, based on:
|
||||
-- http://blog.cartodb.com/jets-and-datelines/
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_GreatCircle(start_point geometry, end_point geometry, max_segment_length NUMERIC DEFAULT 100000)
|
||||
RETURNS geometry AS $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_GreatCircle(start_point @postgisschema@.geometry, end_point @postgisschema@.geometry, max_segment_length NUMERIC DEFAULT 100000)
|
||||
RETURNS @postgisschema@.geometry AS $$
|
||||
DECLARE
|
||||
line geometry;
|
||||
line @postgisschema@.geometry;
|
||||
BEGIN
|
||||
line = ST_Segmentize(
|
||||
ST_Makeline(
|
||||
line = @postgisschema@.ST_Segmentize(
|
||||
@postgisschema@.ST_Makeline(
|
||||
start_point,
|
||||
end_point
|
||||
)::geography,
|
||||
max_segment_length
|
||||
)::geometry;
|
||||
|
||||
IF ST_XMax(line) - ST_XMin(line) > 180 THEN
|
||||
line = ST_Difference(
|
||||
ST_Shift_Longitude(line),
|
||||
ST_Buffer(ST_GeomFromText('LINESTRING(180 90, 180 -90)', 4326), 0.00001)
|
||||
IF @postgisschema@.ST_XMax(line) - @postgisschema@.ST_XMin(line) > 180 THEN
|
||||
line = @postgisschema@.ST_Difference(
|
||||
@postgisschema@.ST_ShiftLongitude(line),
|
||||
@postgisschema@.ST_Buffer(@postgisschema@.ST_GeomFromText('LINESTRING(180 90, 180 -90)', 4326), 0.00001)
|
||||
);
|
||||
END IF;
|
||||
RETURN line;
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
-- Creates a new group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_CreateGroup(group_name text)
|
||||
FUNCTION @extschema@.CDB_Group_CreateGroup(group_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
EXECUTE format('CREATE ROLE %I NOLOGIN;', group_role);
|
||||
PERFORM cartodb._CDB_Group_CreateGroup_API(group_name, group_role);
|
||||
PERFORM @extschema@._CDB_Group_CreateGroup_API(group_name, group_role);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -23,72 +23,72 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Not even the role creator can drop the role and the objects it owns.
|
||||
-- All group owned objects by the group are permissions.
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_DropGroup(group_name text)
|
||||
FUNCTION @extschema@.CDB_Group_DropGroup(group_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
EXECUTE format('DROP OWNED BY %I', group_role);
|
||||
EXECUTE format('DROP ROLE IF EXISTS %I', group_role);
|
||||
PERFORM cartodb._CDB_Group_DropGroup_API(group_name);
|
||||
PERFORM @extschema@._CDB_Group_DropGroup_API(group_name);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Renames a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_RenameGroup(old_group_name text, new_group_name text)
|
||||
FUNCTION @extschema@.CDB_Group_RenameGroup(old_group_name text, new_group_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
old_group_role TEXT;
|
||||
new_group_role TEXT;
|
||||
BEGIN
|
||||
old_group_role = cartodb._CDB_Group_GroupRole(old_group_name);
|
||||
new_group_role = cartodb._CDB_Group_GroupRole(new_group_name);
|
||||
old_group_role = @extschema@._CDB_Group_GroupRole(old_group_name);
|
||||
new_group_role = @extschema@._CDB_Group_GroupRole(new_group_name);
|
||||
EXECUTE format('ALTER ROLE %I RENAME TO %I', old_group_role, new_group_role);
|
||||
PERFORM cartodb._CDB_Group_RenameGroup_API(old_group_name, new_group_name, new_group_role);
|
||||
PERFORM @extschema@._CDB_Group_RenameGroup_API(old_group_name, new_group_name, new_group_role);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Adds users to a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_AddUsers(group_name text, usernames text[])
|
||||
FUNCTION @extschema@.CDB_Group_AddUsers(group_name text, usernames text[])
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
user_role TEXT;
|
||||
username TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
foreach username in array usernames
|
||||
loop
|
||||
user_role := cartodb._CDB_User_RoleFromUsername(username);
|
||||
user_role := @extschema@._CDB_User_RoleFromUsername(username);
|
||||
IF(group_role IS NULL OR user_role IS NULL)
|
||||
THEN
|
||||
RAISE EXCEPTION 'Group role (%) and user role (%) must be already existing', group_role, user_role;
|
||||
END IF;
|
||||
EXECUTE format('GRANT %I TO %I', group_role, user_role);
|
||||
end loop;
|
||||
PERFORM cartodb._CDB_Group_AddUsers_API(group_name, usernames);
|
||||
PERFORM @extschema@._CDB_Group_AddUsers_API(group_name, usernames);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Removes users from a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_RemoveUsers(group_name text, usernames text[])
|
||||
FUNCTION @extschema@.CDB_Group_RemoveUsers(group_name text, usernames text[])
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
user_role TEXT;
|
||||
username TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
foreach username in array usernames
|
||||
loop
|
||||
user_role := cartodb._CDB_User_RoleFromUsername(username);
|
||||
user_role := @extschema@._CDB_User_RoleFromUsername(username);
|
||||
EXECUTE format('REVOKE %I FROM %I', group_role, user_role);
|
||||
end loop;
|
||||
PERFORM cartodb._CDB_Group_RemoveUsers_API(group_name, usernames);
|
||||
PERFORM @extschema@._CDB_Group_RemoveUsers_API(group_name, usernames);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -100,67 +100,67 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Grants table read permission to a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_Table_GrantRead(group_name text, username text, table_name text)
|
||||
FUNCTION @extschema@.CDB_Group_Table_GrantRead(group_name text, username text, table_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
PERFORM cartodb._CDB_Group_Table_GrantRead(group_name, username, table_name, true);
|
||||
PERFORM @extschema@._CDB_Group_Table_GrantRead(group_name, username, table_name, true);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_Table_GrantRead(group_name text, username text, table_name text, sync boolean)
|
||||
FUNCTION @extschema@._CDB_Group_Table_GrantRead(group_name text, username text, table_name text, sync boolean)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', username, group_role);
|
||||
EXECUTE format('GRANT SELECT ON TABLE %I.%I TO %I', username, table_name, group_role );
|
||||
IF(sync) THEN
|
||||
PERFORM cartodb._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'r');
|
||||
PERFORM @extschema@._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'r');
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Grants table write permission to a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text)
|
||||
FUNCTION @extschema@.CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
PERFORM cartodb._CDB_Group_Table_GrantReadWrite(group_name, username, table_name, true);
|
||||
PERFORM @extschema@._CDB_Group_Table_GrantReadWrite(group_name, username, table_name, true);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text, sync boolean)
|
||||
FUNCTION @extschema@._CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text, sync boolean)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', username, group_role);
|
||||
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I', username, table_name, group_role);
|
||||
PERFORM cartodb._CDB_Group_TableSequences_Permission(group_name, username, table_name, true);
|
||||
PERFORM @extschema@._CDB_Group_TableSequences_Permission(group_name, username, table_name, true);
|
||||
IF(sync) THEN
|
||||
PERFORM cartodb._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'w');
|
||||
PERFORM @extschema@._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'w');
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Granting and revoking permissions on sequences
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_TableSequences_Permission(group_name text, username text, table_name text, do_grant bool)
|
||||
FUNCTION @extschema@._CDB_Group_TableSequences_Permission(group_name text, username text, table_name text, do_grant bool)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
column_name TEXT;
|
||||
sequence_name TEXT;
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
FOR column_name IN EXECUTE 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = current_database() AND TABLE_SCHEMA = $1 AND TABLE_NAME = $2 AND COLUMN_DEFAULT LIKE ''nextval%''' USING username, table_name
|
||||
LOOP
|
||||
EXECUTE format('SELECT PG_GET_SERIAL_SEQUENCE(''%I.%I'', ''%I'')', username, table_name, column_name) INTO sequence_name;
|
||||
@@ -179,26 +179,26 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Revokes all permissions on a table from a group
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Group_Table_RevokeAll(group_name text, username text, table_name text)
|
||||
FUNCTION @extschema@.CDB_Group_Table_RevokeAll(group_name text, username text, table_name text)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
PERFORM cartodb._CDB_Group_Table_RevokeAll(group_name, username, table_name, true);
|
||||
PERFORM @extschema@._CDB_Group_Table_RevokeAll(group_name, username, table_name, true);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_Table_RevokeAll(group_name text, username text, table_name text, sync boolean)
|
||||
FUNCTION @extschema@._CDB_Group_Table_RevokeAll(group_name text, username text, table_name text, sync boolean)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
BEGIN
|
||||
group_role := cartodb._CDB_Group_GroupRole(group_name);
|
||||
group_role := @extschema@._CDB_Group_GroupRole(group_name);
|
||||
EXECUTE format('REVOKE ALL ON TABLE %I.%I FROM %I', username, table_name, group_role);
|
||||
PERFORM cartodb._CDB_Group_TableSequences_Permission(group_name, username, table_name, false);
|
||||
PERFORM @extschema@._CDB_Group_TableSequences_Permission(group_name, username, table_name, false);
|
||||
IF(sync) THEN
|
||||
PERFORM cartodb._CDB_Group_Table_RevokeAllPermission_API(group_name, username, table_name);
|
||||
PERFORM @extschema@._CDB_Group_Table_RevokeAllPermission_API(group_name, username, table_name);
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
@@ -208,14 +208,14 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-----------------------
|
||||
-- Given a group name returns a role. group_name must be a valid PostgreSQL idenfifier. See http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_GroupRole(group_name text)
|
||||
FUNCTION @extschema@._CDB_Group_GroupRole(group_name text)
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
group_role TEXT;
|
||||
prefix TEXT;
|
||||
max_length constant INTEGER := 63;
|
||||
BEGIN
|
||||
prefix = format('%s_g_', cartodb._CDB_Group_ShortDatabaseName());
|
||||
prefix = format('%s_g_', @extschema@._CDB_Group_ShortDatabaseName());
|
||||
group_role := format('%s%s', prefix, group_name);
|
||||
IF LENGTH(group_role) > max_length
|
||||
THEN
|
||||
@@ -227,7 +227,7 @@ $$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- Returns the first owner of the schema matching username. Organization user schemas must have one only owner.
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_User_RoleFromUsername(username text)
|
||||
FUNCTION @extschema@._CDB_User_RoleFromUsername(username text)
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
user_role TEXT;
|
||||
@@ -241,7 +241,7 @@ $$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- Database names are too long, we need a shorter version for composing role names
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_ShortDatabaseName()
|
||||
FUNCTION @extschema@._CDB_Group_ShortDatabaseName()
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
short_database_name TEXT;
|
||||
|
||||
@@ -2,30 +2,30 @@
|
||||
-- GROUP METADATA API FUNCTIONS
|
||||
--
|
||||
-- Meant to be used by CDB_Group_* functions to sync data with the editor.
|
||||
-- Requires configuration parameter. Example: SELECT cartodb.CDB_Conf_SetConf('groups_api', '{ "host": "127.0.0.1", "port": 3000, "timeout": 10, "username": "extension", "password": "elephant" }');
|
||||
-- Requires configuration parameter. Example: SELECT @extschema@.CDB_Conf_SetConf('groups_api', '{ "host": "127.0.0.1", "port": 3000, "timeout": 10, "username": "extension", "password": "elephant" }');
|
||||
----------------------------------
|
||||
|
||||
-- TODO: delete this development cleanup before final merge
|
||||
DROP FUNCTION IF EXISTS cartodb.CDB_Group_AddMember(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS cartodb.CDB_Group_RemoveMember(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS cartodb._CDB_Group_AddMember_API(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS cartodb._CDB_Group_RemoveMember_API(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_Group_AddMember(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_Group_RemoveMember(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_AddMember_API(group_name text, username text);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_RemoveMember_API(group_name text, username text);
|
||||
|
||||
-- Sends the create group request
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_CreateGroup_API(group_name text, group_role text)
|
||||
FUNCTION @extschema@._CDB_Group_CreateGroup_API(group_name text, group_role text)
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
|
||||
url = '/api/v1/databases/{0}/groups'
|
||||
body = '{ "name": "%s", "database_role": "%s" }' % (group_name, group_role)
|
||||
query = "select cartodb._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
query = "select @extschema@._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_DropGroup_API(group_name text)
|
||||
FUNCTION @extschema@._CDB_Group_DropGroup_API(group_name text)
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
@@ -33,12 +33,12 @@ $$
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s' % (urllib.pathname2url(group_name))
|
||||
|
||||
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '', '{204, 404}') as response_status" % url
|
||||
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '', '{204, 404}') as response_status" % url
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_RenameGroup_API(old_group_name text, new_group_name text, new_group_role text)
|
||||
FUNCTION @extschema@._CDB_Group_RenameGroup_API(old_group_name text, new_group_name text, new_group_role text)
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
@@ -46,12 +46,12 @@ $$
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s' % (urllib.pathname2url(old_group_name))
|
||||
body = '{ "name": "%s", "database_role": "%s" }' % (new_group_name, new_group_role)
|
||||
query = "select cartodb._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
query = "select @extschema@._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_AddUsers_API(group_name text, usernames text[])
|
||||
FUNCTION @extschema@._CDB_Group_AddUsers_API(group_name text, usernames text[])
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
@@ -59,12 +59,12 @@ $$
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s/users' % (urllib.pathname2url(group_name))
|
||||
body = "{ \"users\": [\"%s\"] }" % "\",\"".join(usernames)
|
||||
query = "select cartodb._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
query = "select @extschema@._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_RemoveUsers_API(group_name text, usernames text[])
|
||||
FUNCTION @extschema@._CDB_Group_RemoveUsers_API(group_name text, usernames text[])
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
@@ -72,20 +72,20 @@ $$
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s/users' % (urllib.pathname2url(group_name))
|
||||
body = "{ \"users\": [\"%s\"] }" % "\",\"".join(usernames)
|
||||
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '%s', '{200, 404}') as response_status" % (url, body)
|
||||
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '%s', '{200, 404}') as response_status" % (url, body)
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
-- Needed for dropping type
|
||||
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS _CDB_Group_API_Params;
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_Table_GrantPermission_API(group_name text, username text, table_name text, access text)
|
||||
FUNCTION @extschema@._CDB_Group_Table_GrantPermission_API(group_name text, username text, table_name text, access text)
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
@@ -93,39 +93,39 @@ $$
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (urllib.pathname2url(group_name), username, table_name)
|
||||
body = '{ "access": "%s" }' % access
|
||||
query = "select cartodb._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
query = "select @extschema@._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
-- Needed for dropping type
|
||||
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS _CDB_Group_API_Params;
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_Table_RevokeAllPermission_API(group_name text, username text, table_name text)
|
||||
FUNCTION @extschema@._CDB_Group_Table_RevokeAllPermission_API(group_name text, username text, table_name text)
|
||||
RETURNS VOID AS
|
||||
$$
|
||||
import string
|
||||
import urllib
|
||||
|
||||
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (urllib.pathname2url(group_name), username, table_name)
|
||||
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '', '{200, 404}') as response_status" % url
|
||||
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '', '{200, 404}') as response_status" % url
|
||||
plpy.execute(query)
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
DO LANGUAGE 'plpgsql' $$
|
||||
BEGIN
|
||||
-- Needed for dropping type
|
||||
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS _CDB_Group_API_Params;
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
|
||||
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TYPE _CDB_Group_API_Params AS (
|
||||
CREATE TYPE @extschema@._CDB_Group_API_Params AS (
|
||||
host text,
|
||||
port int,
|
||||
timeout int,
|
||||
@@ -135,21 +135,21 @@ CREATE TYPE _CDB_Group_API_Params AS (
|
||||
-- This must be explicitally extracted because "composite types are currently not supported".
|
||||
-- See http://www.postgresql.org/docs/9.3/static/plpython-database.html.
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_API_Conf()
|
||||
RETURNS _CDB_Group_API_Params AS
|
||||
FUNCTION @extschema@._CDB_Group_API_Conf()
|
||||
RETURNS @extschema@._CDB_Group_API_Params AS
|
||||
$$
|
||||
conf = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('groups_api') conf")[0]['conf']
|
||||
conf = plpy.execute("SELECT @extschema@.CDB_Conf_GetConf('groups_api') conf")[0]['conf']
|
||||
if conf is None:
|
||||
return None
|
||||
else:
|
||||
import json
|
||||
params = json.loads(conf)
|
||||
auth = 'Basic %s' % plpy.execute("SELECT cartodb._CDB_Group_API_Auth('%s', '%s') as auth" % (params['username'], params['password']))[0]['auth']
|
||||
auth = 'Basic %s' % plpy.execute("SELECT @extschema@._CDB_Group_API_Auth('%s', '%s') as auth" % (params['username'], params['password']))[0]['auth']
|
||||
return { "host": params['host'], "port": params['port'], 'timeout': params['timeout'], 'auth': auth }
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_API_Auth(username text, password text)
|
||||
FUNCTION @extschema@._CDB_Group_API_Auth(username text, password text)
|
||||
RETURNS TEXT AS
|
||||
$$
|
||||
import base64
|
||||
@@ -158,12 +158,12 @@ $$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- url must contain a '%s' placeholder that will be replaced by current_database, for security reasons.
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Group_API_Request(method text, url text, body text, valid_return_codes int[])
|
||||
FUNCTION @extschema@._CDB_Group_API_Request(method text, url text, body text, valid_return_codes int[])
|
||||
RETURNS int AS
|
||||
$$
|
||||
import httplib
|
||||
|
||||
params = plpy.execute("select c.host, c.port, c.timeout, c.auth from cartodb._CDB_Group_API_Conf() c;")[0]
|
||||
params = plpy.execute("select c.host, c.port, c.timeout, c.auth from @extschema@._CDB_Group_API_Conf() c;")[0]
|
||||
if params['host'] is None:
|
||||
return None
|
||||
|
||||
@@ -192,4 +192,4 @@ $$
|
||||
|
||||
return None
|
||||
$$ LANGUAGE 'plpythonu' VOLATILE PARALLEL UNSAFE;
|
||||
revoke all on function cartodb._CDB_Group_API_Request(text, text, text, int[]) from public;
|
||||
revoke all on function @extschema@._CDB_Group_API_Request(text, text, text, int[]) from public;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_HeadsTailsBins ( in_array NUMERIC[], breaks INT) RETURNS NUMERIC[] as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_HeadsTailsBins ( in_array NUMERIC[], breaks INT) RETURNS NUMERIC[] as $$
|
||||
DECLARE
|
||||
element_count INT4;
|
||||
arr_mean numeric;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- UTF8 safe and length aware. Find a unique identifier with a given prefix
|
||||
-- and/or suffix and withing a schema. If a schema is not specified, the identifier
|
||||
-- is guaranteed to be unique for all schemas.
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Unique_Identifier(prefix TEXT, relname TEXT, suffix TEXT, schema TEXT DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Identifier(prefix TEXT, relname TEXT, suffix TEXT, schema TEXT DEFAULT NULL)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -24,10 +24,10 @@ BEGIN
|
||||
usedspace := usedspace + coalesce(octet_length(prefix), 0);
|
||||
usedspace := usedspace + coalesce(octet_length(suffix), 0);
|
||||
|
||||
candrelname := _CDB_Octet_Truncate(relname, maxlen - usedspace);
|
||||
candrelname := @extschema@._CDB_Octet_Truncate(relname, maxlen - usedspace);
|
||||
|
||||
IF candrelname = '' THEN
|
||||
PERFORM _CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Identifier');
|
||||
PERFORM @extschema@._CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Identifier');
|
||||
END IF;
|
||||
|
||||
ident := coalesce(prefix, '') || candrelname || coalesce(suffix, '');
|
||||
@@ -59,14 +59,14 @@ BEGIN
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
PERFORM _CDB_Error('looping too far', '_CDB_Unique_Identifier');
|
||||
PERFORM @extschema@._CDB_Error('looping too far', '_CDB_Unique_Identifier');
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
-- UTF8 safe and length aware. Find a unique identifier for a column with a given prefix
|
||||
-- and/or suffix based on colname and within a relation specified via reloid.
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Unique_Column_Identifier(prefix TEXT, colname TEXT, suffix TEXT, reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Column_Identifier(prefix TEXT, colname TEXT, suffix TEXT, reloid REGCLASS)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -85,10 +85,10 @@ BEGIN
|
||||
usedspace := usedspace + coalesce(octet_length(prefix), 0);
|
||||
usedspace := usedspace + coalesce(octet_length(suffix), 0);
|
||||
|
||||
candcolname := _CDB_Octet_Truncate(colname, maxlen - usedspace);
|
||||
candcolname := @extschema@._CDB_Octet_Truncate(colname, maxlen - usedspace);
|
||||
|
||||
IF candcolname = '' THEN
|
||||
PERFORM _CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Column_Identifier');
|
||||
PERFORM @extschema@._CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Column_Identifier');
|
||||
END IF;
|
||||
|
||||
ident := coalesce(prefix, '') || candcolname || coalesce(suffix, '');
|
||||
@@ -114,14 +114,14 @@ BEGIN
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
PERFORM _CDB_Error('looping too far', '_CDB_Unique_Column_Identifier');
|
||||
PERFORM @extschema@._CDB_Error('looping too far', '_CDB_Unique_Column_Identifier');
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL SAFE;
|
||||
|
||||
|
||||
-- Truncates a given string to a max_octets octets taking care
|
||||
-- not to leave characters in half. UTF8 safe.
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Octet_Truncate(string TEXT, max_octets INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Octet_Truncate(string TEXT, max_octets INTEGER)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -158,3 +158,20 @@ BEGIN
|
||||
RETURN left(string, (i - 1));
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
|
||||
-- Checks if a given text representing a qualified or unqualified table name (relation)
|
||||
-- actually exists in the database. It is meant to be used as a guard for other function/queries.
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Table_Exists(table_name_with_optional_schema TEXT)
|
||||
RETURNS bool
|
||||
AS $$
|
||||
DECLARE
|
||||
table_exists bool := false;
|
||||
BEGIN
|
||||
table_exists := EXISTS(SELECT * FROM pg_class WHERE table_name_with_optional_schema::regclass::oid = oid AND relkind = 'r');
|
||||
RETURN table_exists;
|
||||
EXCEPTION
|
||||
WHEN invalid_schema_name OR undefined_table THEN
|
||||
RETURN false;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
-- Return an Hexagon with given center and side (or maximal radius)
|
||||
CREATE OR REPLACE FUNCTION CDB_MakeHexagon(center GEOMETRY, radius FLOAT8)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_MakeHexagon(center GEOMETRY, radius FLOAT8)
|
||||
RETURNS GEOMETRY
|
||||
AS $$
|
||||
SELECT ST_MakePolygon(ST_MakeLine(geom))
|
||||
SELECT @postgisschema@.ST_MakePolygon(@postgisschema@.ST_MakeLine(geom))
|
||||
FROM
|
||||
(
|
||||
SELECT (ST_DumpPoints(ST_ExteriorRing(ST_Buffer($1, $2, 3)))).*
|
||||
SELECT (@postgisschema@.ST_DumpPoints(@postgisschema@.ST_ExteriorRing(@postgisschema@.ST_Buffer($1, $2, 3)))).*
|
||||
) as points
|
||||
WHERE path[1] % 2 != 0
|
||||
$$ LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
|
||||
-- In older versions of the extension, CDB_HexagonGrid had a different signature
|
||||
DROP FUNCTION IF EXISTS cartodb.CDB_HexagonGrid(GEOMETRY, FLOAT8, GEOMETRY);
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_HexagonGrid(GEOMETRY, FLOAT8, GEOMETRY);
|
||||
|
||||
--
|
||||
-- Fill given extent with an hexagonal coverage
|
||||
@@ -35,7 +35,7 @@ DROP FUNCTION IF EXISTS cartodb.CDB_HexagonGrid(GEOMETRY, FLOAT8, GEOMETRY);
|
||||
-- and exception will occur.
|
||||
----
|
||||
-- DROP FUNCTION IF EXISTS CDB_HexagonGrid(ext GEOMETRY, side FLOAT8);
|
||||
CREATE OR REPLACE FUNCTION CDB_HexagonGrid(ext GEOMETRY, side FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_HexagonGrid(ext GEOMETRY, side FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
|
||||
RETURNS SETOF GEOMETRY
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -80,11 +80,11 @@ BEGIN
|
||||
yoff := 0;
|
||||
|
||||
IF origin IS NOT NULL THEN
|
||||
IF ST_SRID(origin) != srid THEN
|
||||
IF @postgisschema@.ST_SRID(origin) != srid THEN
|
||||
RAISE EXCEPTION 'SRID mismatch between extent (%) and origin (%)', srid, ST_SRID(origin);
|
||||
END IF;
|
||||
xoff := ST_X(origin);
|
||||
yoff := ST_Y(origin);
|
||||
xoff := @postgisschema@.ST_X(origin);
|
||||
yoff := @postgisschema@.ST_Y(origin);
|
||||
END IF;
|
||||
|
||||
RAISE DEBUG 'X offset: %', xoff;
|
||||
@@ -96,19 +96,19 @@ BEGIN
|
||||
RAISE DEBUG 'Y grid size: %', ygrd;
|
||||
|
||||
-- Tweak horizontal start on hstep*2 grid from origin
|
||||
hskip := ceil((ST_XMin(ext)-xoff)/hstep);
|
||||
hskip := ceil((@postgisschema@.ST_XMin(ext)-xoff)/hstep);
|
||||
RAISE DEBUG 'hskip: %', hskip;
|
||||
hstart := xoff + hskip*hstep;
|
||||
RAISE DEBUG 'hstart: %', hstart;
|
||||
|
||||
-- Tweak vertical start on hstep grid from origin
|
||||
vstart := yoff + ceil((ST_Ymin(ext)-yoff)/vstep)*vstep;
|
||||
vstart := yoff + ceil((@postgisschema@.ST_Ymin(ext)-yoff)/vstep)*vstep;
|
||||
RAISE DEBUG 'vstart: %', vstart;
|
||||
|
||||
hend := ST_XMax(ext);
|
||||
vend := ST_YMax(ext);
|
||||
hend := @postgisschema@.ST_XMax(ext);
|
||||
vend := @postgisschema@.ST_YMax(ext);
|
||||
|
||||
IF vstart - (vstep/2.0) < ST_YMin(ext) THEN
|
||||
IF vstart - (vstep/2.0) < @postgisschema@.ST_YMin(ext) THEN
|
||||
vstartary := ARRAY[ vstart + (vstep/2.0), vstart ];
|
||||
ELSE
|
||||
vstartary := ARRAY[ vstart - (vstep/2.0), vstart ];
|
||||
@@ -125,21 +125,21 @@ BEGIN
|
||||
RAISE DEBUG 'vstartary: % : %', vstartary[1], vstartary[2];
|
||||
RAISE DEBUG 'vstartidx: %', vstartidx;
|
||||
|
||||
c := ST_SetSRID(ST_MakePoint(hstart, vstartary[vstartidx+1]), srid);
|
||||
h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
|
||||
c := @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(hstart, vstartary[vstartidx+1]), srid);
|
||||
h := @postgisschema@.ST_SnapToGrid(@extschema@.CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
|
||||
vstartidx := (vstartidx + 1) % 2;
|
||||
WHILE ST_X(c) < hend LOOP -- over X
|
||||
WHILE @postgisschema@.ST_X(c) < hend LOOP -- over X
|
||||
--RAISE DEBUG 'X loop starts, center point: %', ST_AsText(c);
|
||||
WHILE ST_Y(c) < vend LOOP -- over Y
|
||||
WHILE @postgisschema@.ST_Y(c) < vend LOOP -- over Y
|
||||
--RAISE DEBUG 'Center: %', ST_AsText(c);
|
||||
--h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
|
||||
RETURN NEXT h;
|
||||
h := ST_SnapToGrid(ST_Translate(h, 0, vstep), xoff, yoff, xgrd, ygrd);
|
||||
c := ST_Translate(c, 0, vstep); -- TODO: drop ?
|
||||
h := @postgisschema@.ST_SnapToGrid(ST_Translate(h, 0, vstep), xoff, yoff, xgrd, ygrd);
|
||||
c := @postgisschema@.ST_Translate(c, 0, vstep); -- TODO: drop ?
|
||||
END LOOP;
|
||||
-- TODO: translate h direcly ...
|
||||
c := ST_SetSRID(ST_MakePoint(ST_X(c)+hstep, vstartary[vstartidx+1]), srid);
|
||||
h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
|
||||
c := @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(ST_X(c)+hstep, vstartary[vstartidx+1]), srid);
|
||||
h := @postgisschema@.ST_SnapToGrid(@extschema@.CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
|
||||
vstartidx := (vstartidx + 1) % 2;
|
||||
END LOOP;
|
||||
|
||||
|
||||
@@ -10,212 +10,337 @@
|
||||
--
|
||||
-- @param invert Optional wheter to return the top of each bin (default)
|
||||
-- or the bottom. BOOLEAN, default=FALSE.
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_JenksBins(in_array NUMERIC[], breaks INT, iterations INT DEFAULT 0, invert BOOLEAN DEFAULT FALSE)
|
||||
RETURNS NUMERIC[] as
|
||||
$$
|
||||
DECLARE
|
||||
in_matrix NUMERIC[][];
|
||||
in_unique_count BIGINT;
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_JenksBins ( in_array NUMERIC[], breaks INT, iterations INT DEFAULT 5, invert BOOLEAN DEFAULT FALSE) RETURNS NUMERIC[] as $$
|
||||
DECLARE
|
||||
element_count INT4;
|
||||
shuffles INT;
|
||||
arr_mean NUMERIC;
|
||||
sdam NUMERIC;
|
||||
|
||||
i INT;
|
||||
bot INT;
|
||||
top INT;
|
||||
|
||||
tops INT[];
|
||||
classes INT[][];
|
||||
i INT := 1; j INT := 1;
|
||||
j INT := 1;
|
||||
curr_result NUMERIC[];
|
||||
best_result NUMERIC[];
|
||||
seedtarget TEXT;
|
||||
quant NUMERIC[];
|
||||
shuffles INT;
|
||||
|
||||
BEGIN
|
||||
-- get the total size of our row
|
||||
element_count := array_length(in_array, 1); --array_upper(in_array, 1) - array_lower(in_array, 1);
|
||||
-- ensure the ordering of in_array
|
||||
SELECT array_agg(e) INTO in_array FROM (SELECT unnest(in_array) e ORDER BY e) x;
|
||||
-- stop if no rows
|
||||
IF element_count IS NULL THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- stop if our breaks are more than our input array size
|
||||
IF element_count < breaks THEN
|
||||
RETURN in_array;
|
||||
END IF;
|
||||
-- We clean the input array (remove NULLs) and create 2 arrays
|
||||
-- [1] contains the unique values in in_array
|
||||
-- [2] contains the number of appearances of those unique values
|
||||
SELECT ARRAY[array_agg(value), array_agg(count)] FROM
|
||||
(
|
||||
SELECT value, count(1)::numeric as count
|
||||
FROM unnest(in_array) AS value
|
||||
WHERE value is NOT NULL
|
||||
GROUP BY value
|
||||
ORDER BY value
|
||||
) __clean_array_q INTO in_matrix;
|
||||
|
||||
shuffles := LEAST(GREATEST(floor(2500000.0/(element_count::float*iterations::float)), 1), 750)::int;
|
||||
-- get our mean value
|
||||
SELECT avg(v) INTO arr_mean FROM ( SELECT unnest(in_array) as v ) x;
|
||||
-- Get the number of unique values
|
||||
in_unique_count := array_length(in_matrix[1:1], 2);
|
||||
|
||||
-- assume best is actually Quantile
|
||||
SELECT CDB_QuantileBins(in_array, breaks) INTO quant;
|
||||
|
||||
-- if data is very very large, just return quant and be done
|
||||
IF element_count > 5000000 THEN
|
||||
RETURN quant;
|
||||
IF in_unique_count IS NULL THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
-- change quant into bottom, top markers
|
||||
LOOP
|
||||
IF i = 1 THEN
|
||||
bot = 1;
|
||||
ELSE
|
||||
-- use last top to find this bot
|
||||
bot = top+1;
|
||||
END IF;
|
||||
IF i = breaks THEN
|
||||
top = element_count;
|
||||
IF in_unique_count <= breaks THEN
|
||||
-- There isn't enough distinct values for the requested breaks
|
||||
RETURN ARRAY(Select unnest(in_matrix[1:1])) _a;
|
||||
END IF;
|
||||
|
||||
-- If not declated explicitly we iterate based on the length of the array
|
||||
IF iterations < 1 THEN
|
||||
-- This is based on a 'looks fine' heuristic
|
||||
iterations := log(in_unique_count)::integer + 1;
|
||||
END IF;
|
||||
|
||||
-- We set the number of shuffles per iteration as the number of unique values but
|
||||
-- this is just another 'looks fine' heuristic
|
||||
shuffles := in_unique_count;
|
||||
|
||||
-- Get the mean value of the whole vector (already ignores NULLs)
|
||||
SELECT avg(v) INTO arr_mean FROM ( SELECT unnest(in_array) as v ) x;
|
||||
|
||||
-- Calculate the sum of squared deviations from the array mean (SDAM).
|
||||
SELECT sum(((arr_mean - v)^2) * w) INTO sdam FROM (
|
||||
SELECT unnest(in_matrix[1:1]) as v, unnest(in_matrix[2:2]) as w
|
||||
) x;
|
||||
|
||||
-- To start, we create ranges with approximately the same amount of different values
|
||||
top := 0;
|
||||
i := 1;
|
||||
LOOP
|
||||
bot := top + 1;
|
||||
top := ROUND(i * in_unique_count::numeric / breaks::NUMERIC);
|
||||
|
||||
IF i = 1 THEN
|
||||
classes = ARRAY[ARRAY[bot,top]];
|
||||
ELSE
|
||||
SELECT count(*) INTO top FROM ( SELECT unnest(in_array) as v) x WHERE v <= quant[i];
|
||||
END IF;
|
||||
IF i = 1 THEN
|
||||
classes = ARRAY[ARRAY[bot,top]];
|
||||
ELSE
|
||||
classes = ARRAY_CAT(classes,ARRAY[bot,top]);
|
||||
classes = ARRAY_CAT(classes, ARRAY[bot,top]);
|
||||
END IF;
|
||||
|
||||
i := i + 1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
i = i+1;
|
||||
END LOOP;
|
||||
|
||||
best_result = CDB_JenksBinsIteration( in_array, breaks, classes, invert, element_count, arr_mean, shuffles);
|
||||
best_result = @extschema@.CDB_JenksBinsIteration(in_matrix, breaks, classes, invert, sdam, shuffles);
|
||||
|
||||
--set the seed so we can ensure the same results
|
||||
SELECT setseed(0.4567) INTO seedtarget;
|
||||
--loop through random starting positions
|
||||
LOOP
|
||||
IF j > iterations-1 THEN EXIT; END IF;
|
||||
IF j > iterations-1 THEN EXIT; END IF;
|
||||
i = 1;
|
||||
tops = ARRAY[element_count];
|
||||
tops = ARRAY[in_unique_count];
|
||||
LOOP
|
||||
IF i = breaks THEN EXIT; END IF;
|
||||
SELECT array_agg(distinct e) INTO tops FROM (SELECT unnest(array_cat(tops, ARRAY[floor(random()*element_count::float)::int])) as e ORDER BY e) x WHERE e != 1;
|
||||
i = array_length(tops, 1);
|
||||
END LOOP;
|
||||
IF i = breaks THEN EXIT; END IF;
|
||||
SELECT array_agg(distinct e) INTO tops FROM (
|
||||
SELECT unnest(array_cat(tops, ARRAY[trunc(random() * in_unique_count::float8)::int + 1])) as e ORDER BY e
|
||||
) x;
|
||||
i = array_length(tops, 1);
|
||||
END LOOP;
|
||||
top := 0;
|
||||
i = 1;
|
||||
LOOP
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
IF i = 1 THEN
|
||||
bot = 1;
|
||||
ELSE
|
||||
bot = top+1;
|
||||
END IF;
|
||||
LOOP
|
||||
bot := top + 1;
|
||||
top = tops[i];
|
||||
IF i = 1 THEN
|
||||
classes = ARRAY[ARRAY[bot,top]];
|
||||
ELSE
|
||||
classes = ARRAY_CAT(classes,ARRAY[bot,top]);
|
||||
IF i = 1 THEN
|
||||
classes = ARRAY[ARRAY[bot,top]];
|
||||
ELSE
|
||||
classes = ARRAY_CAT(classes, ARRAY[bot,top]);
|
||||
END IF;
|
||||
i := i+1;
|
||||
END LOOP;
|
||||
curr_result = CDB_JenksBinsIteration( in_array, breaks, classes, invert, element_count, arr_mean, shuffles);
|
||||
|
||||
i := i+1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
END LOOP;
|
||||
|
||||
curr_result = @extschema@.CDB_JenksBinsIteration(in_matrix, breaks, classes, invert, sdam, shuffles);
|
||||
|
||||
IF curr_result[1] > best_result[1] THEN
|
||||
best_result = curr_result;
|
||||
j = j-1; -- if we found a better result, add one more search
|
||||
END IF;
|
||||
|
||||
j = j+1;
|
||||
END LOOP;
|
||||
|
||||
RETURN (best_result)[2:array_upper(best_result, 1)];
|
||||
END;
|
||||
$$ language plpgsql VOLATILE PARALLEL RESTRICTED;
|
||||
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL RESTRICTED;
|
||||
|
||||
|
||||
--
|
||||
-- Perform a single iteration of the Jenks classification
|
||||
--
|
||||
-- Returns an array with:
|
||||
-- - First element: gvf
|
||||
-- - Second to 2+n: Category limits
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_JenksBinsIteration ( in_matrix NUMERIC[], breaks INT, classes INT[], invert BOOLEAN, element_count INT4, arr_mean NUMERIC, max_search INT); -- Old signature
|
||||
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_JenksBinsIteration ( in_matrix NUMERIC[], breaks INT, classes INT[], invert BOOLEAN, sdam NUMERIC, max_search INT DEFAULT 50) RETURNS NUMERIC[] as $$
|
||||
DECLARE
|
||||
i INT;
|
||||
iterations INT = 0;
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_JenksBinsIteration ( in_array NUMERIC[], breaks INT, classes INT[][], invert BOOLEAN, element_count INT4, arr_mean NUMERIC, max_search INT DEFAULT 50) RETURNS NUMERIC[] as $$
|
||||
DECLARE
|
||||
tmp_val numeric;
|
||||
new_classes int[][];
|
||||
tmp_class int[];
|
||||
i INT := 1;
|
||||
j INT := 1;
|
||||
side INT := 2;
|
||||
sdam numeric;
|
||||
gvf numeric := 0.0;
|
||||
new_gvf numeric;
|
||||
arr_gvf numeric[];
|
||||
class_avg numeric;
|
||||
class_max_i INT;
|
||||
class_min_i INT;
|
||||
class_max numeric;
|
||||
class_min numeric;
|
||||
reply numeric[];
|
||||
BEGIN
|
||||
|
||||
-- Calculate the sum of squared deviations from the array mean (SDAM).
|
||||
SELECT sum((arr_mean - e)^2) INTO sdam FROM ( SELECT unnest(in_array) as e ) x;
|
||||
--Identify the breaks for the lowest GVF
|
||||
LOOP
|
||||
i = 1;
|
||||
LOOP
|
||||
-- get our mean
|
||||
SELECT avg(e) INTO class_avg FROM ( SELECT unnest(in_array[classes[i][1]:classes[i][2]]) as e) x;
|
||||
-- find the deviation
|
||||
SELECT sum((class_avg-e)^2) INTO tmp_val FROM ( SELECT unnest(in_array[classes[i][1]:classes[i][2]]) as e ) x;
|
||||
IF i = 1 THEN
|
||||
arr_gvf = ARRAY[tmp_val];
|
||||
-- init our min/max map for later
|
||||
class_max = arr_gvf[i];
|
||||
class_min = arr_gvf[i];
|
||||
class_min_i = 1;
|
||||
class_max_i = 1;
|
||||
ELSE
|
||||
arr_gvf = array_append(arr_gvf, tmp_val);
|
||||
END IF;
|
||||
i := i+1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
END LOOP;
|
||||
-- calculate our new GVF
|
||||
SELECT sdam-sum(e) INTO new_gvf FROM ( SELECT unnest(arr_gvf) as e ) x;
|
||||
-- if no improvement was made, exit
|
||||
IF new_gvf < gvf THEN EXIT; END IF;
|
||||
gvf = new_gvf;
|
||||
IF j > max_search THEN EXIT; END IF;
|
||||
j = j+1;
|
||||
i = 1;
|
||||
LOOP
|
||||
--establish directionality (uppward through classes or downward)
|
||||
IF arr_gvf[i] < class_min THEN
|
||||
class_min = arr_gvf[i];
|
||||
class_min_i = i;
|
||||
END IF;
|
||||
IF arr_gvf[i] > class_max THEN
|
||||
class_max = arr_gvf[i];
|
||||
class_max_i = i;
|
||||
END IF;
|
||||
i := i+1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
END LOOP;
|
||||
IF class_max_i > class_min_i THEN
|
||||
class_min_i = class_max_i - 1;
|
||||
gvf numeric := 0.0;
|
||||
new_gvf numeric;
|
||||
arr_gvf numeric[];
|
||||
arr_avg numeric[];
|
||||
class_avg numeric;
|
||||
class_dev numeric;
|
||||
|
||||
class_max_i INT = 0;
|
||||
class_min_i INT = 0;
|
||||
dev_max numeric;
|
||||
dev_min numeric;
|
||||
|
||||
best_classes INT[] = classes;
|
||||
best_gvf numeric[];
|
||||
best_avg numeric[];
|
||||
move_elements INT = 1;
|
||||
|
||||
reply numeric[];
|
||||
|
||||
BEGIN
|
||||
|
||||
-- We fill the arrays with the initial values
|
||||
i = 0;
|
||||
LOOP
|
||||
IF i = breaks THEN EXIT; END IF;
|
||||
i = i + 1;
|
||||
|
||||
-- Get class mean
|
||||
SELECT (sum(v * w) / sum(w)) INTO class_avg FROM (
|
||||
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
|
||||
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
|
||||
) x;
|
||||
|
||||
-- Get class deviation
|
||||
SELECT sum((class_avg - v)^2 * w) INTO class_dev FROM (
|
||||
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
|
||||
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
|
||||
) x;
|
||||
|
||||
|
||||
IF i = 1 THEN
|
||||
arr_avg = ARRAY[class_avg];
|
||||
arr_gvf = ARRAY[class_dev];
|
||||
ELSE
|
||||
class_min_i = class_max_i + 1;
|
||||
arr_avg = array_append(arr_avg, class_avg);
|
||||
arr_gvf = array_append(arr_gvf, class_dev);
|
||||
END IF;
|
||||
--Move from higher class to a lower gid order
|
||||
IF class_max_i > class_min_i THEN
|
||||
classes[class_max_i][1] = classes[class_max_i][1] + 1;
|
||||
classes[class_min_i][2] = classes[class_min_i][2] + 1;
|
||||
ELSE -- Move from lower class UP into a higher class by gid
|
||||
classes[class_max_i][2] = classes[class_max_i][2] - 1;
|
||||
classes[class_min_i][1] = classes[class_min_i][1] - 1;
|
||||
END LOOP;
|
||||
|
||||
-- We copy the values to avoid recalculation when a failure happens
|
||||
best_avg = arr_avg;
|
||||
best_gvf = arr_gvf;
|
||||
|
||||
iterations = 0;
|
||||
LOOP
|
||||
IF iterations = max_search THEN EXIT; END IF;
|
||||
iterations = iterations + 1;
|
||||
|
||||
-- calculate our new GVF
|
||||
SELECT sdam - sum(e) INTO new_gvf FROM ( SELECT unnest(arr_gvf) as e ) x;
|
||||
|
||||
-- Check if any improvement was made
|
||||
IF new_gvf <= gvf THEN
|
||||
-- If we were moving too many elements, go back and move less
|
||||
IF move_elements <= 2 OR class_max_i = class_min_i THEN
|
||||
EXIT;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
move_elements = GREATEST(move_elements / 8, 1);
|
||||
|
||||
-- Rollback from saved statuses
|
||||
classes = best_classes;
|
||||
new_gvf = gvf;
|
||||
|
||||
i = class_min_i;
|
||||
LOOP
|
||||
arr_avg[i] = best_avg[i];
|
||||
arr_gvf[i] = best_gvf[i];
|
||||
|
||||
IF i = class_max_i THEN EXIT; END IF;
|
||||
i = i + 1;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
-- We search for the classes with the min and max deviation
|
||||
i = 1;
|
||||
class_min_i = 1;
|
||||
class_max_i = 1;
|
||||
dev_max = arr_gvf[1];
|
||||
dev_min = arr_gvf[1];
|
||||
LOOP
|
||||
IF i = breaks THEN EXIT; END IF;
|
||||
i = i + 1;
|
||||
|
||||
IF arr_gvf[i] < dev_min THEN
|
||||
dev_min = arr_gvf[i];
|
||||
class_min_i = i;
|
||||
ELSE
|
||||
IF arr_gvf[i] > dev_max THEN
|
||||
dev_max = arr_gvf[i];
|
||||
class_max_i = i;
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
|
||||
-- Save best values for comparison and output
|
||||
gvf = new_gvf;
|
||||
best_classes = classes;
|
||||
|
||||
-- Limit the moved elements as to not remove everything from class_max_i
|
||||
move_elements = LEAST(move_elements, classes[class_max_i][2] - classes[class_max_i][1]);
|
||||
|
||||
-- Move `move_elements` from class_max_i to class_min_i
|
||||
IF class_min_i < class_max_i THEN
|
||||
i := class_min_i;
|
||||
LOOP
|
||||
IF i = class_max_i THEN EXIT; END IF;
|
||||
classes[i][2] = classes[i][2] + move_elements;
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
i := class_max_i;
|
||||
LOOP
|
||||
IF i = class_min_i THEN EXIT; END IF;
|
||||
classes[i][1] = classes[i][1] + move_elements;
|
||||
i := i - 1;
|
||||
END LOOP;
|
||||
ELSE
|
||||
i := class_min_i;
|
||||
LOOP
|
||||
IF i = class_max_i THEN EXIT; END IF;
|
||||
classes[i][1] = classes[i][1] - move_elements;
|
||||
i := i - 1;
|
||||
END LOOP;
|
||||
|
||||
i := class_max_i;
|
||||
LOOP
|
||||
IF i = class_min_i THEN EXIT; END IF;
|
||||
classes[i][2] = classes[i][2] - move_elements;
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
-- Recalculate avg and deviation ONLY for the affected classes
|
||||
i = LEAST(class_min_i, class_max_i);
|
||||
class_max_i = GREATEST(class_min_i, class_max_i);
|
||||
class_min_i = i;
|
||||
LOOP
|
||||
SELECT (sum(v * w) / sum(w)) INTO class_avg FROM (
|
||||
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
|
||||
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
|
||||
) x;
|
||||
|
||||
SELECT sum((class_avg - v)^2 * w) INTO class_dev FROM (
|
||||
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
|
||||
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
|
||||
) x;
|
||||
|
||||
-- Save status (in case it's needed for rollback) and store the new one
|
||||
best_avg[i] = arr_avg[i];
|
||||
arr_avg[i] = class_avg;
|
||||
|
||||
best_gvf[i] = arr_gvf[i];
|
||||
arr_gvf[i] = class_dev;
|
||||
|
||||
IF i = class_max_i THEN EXIT; END IF;
|
||||
i = i + 1;
|
||||
END LOOP;
|
||||
|
||||
move_elements = move_elements * 2;
|
||||
|
||||
END LOOP;
|
||||
|
||||
i = 1;
|
||||
LOOP
|
||||
LOOP
|
||||
IF invert = TRUE THEN
|
||||
side = 1; --default returns bottom side of breaks, invert returns top side
|
||||
END IF;
|
||||
reply = array_append(reply, in_array[classes[i][side]]);
|
||||
i = i+1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
END LOOP;
|
||||
|
||||
RETURN array_prepend(gvf, reply);
|
||||
reply = array_append(reply, unnest(in_matrix[1:1][best_classes[i][side]:best_classes[i][side]]));
|
||||
i = i+1;
|
||||
IF i > breaks THEN EXIT; END IF;
|
||||
END LOOP;
|
||||
|
||||
END;
|
||||
$$ language plpgsql IMMUTABLE PARALLEL SAFE;
|
||||
reply = array_prepend(gvf, reply);
|
||||
RETURN reply;
|
||||
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
@@ -7,13 +7,11 @@
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_LatLng (lat NUMERIC, lng NUMERIC) RETURNS geometry as $$
|
||||
-- this function is silly
|
||||
SELECT ST_SetSRID(ST_MakePoint(lng,lat),4326);
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_LatLng (lat NUMERIC, lng NUMERIC) RETURNS @postgisschema@.geometry as $$
|
||||
SELECT @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(lng,lat), 4326);
|
||||
$$ language SQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_LatLng (lat FLOAT8, lng FLOAT8) RETURNS geometry as $$
|
||||
-- this function is silly
|
||||
SELECT ST_SetSRID(ST_MakePoint(lng,lat),4326);
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_LatLng (lat FLOAT8, lng FLOAT8) RETURNS @postgisschema@.geometry as $$
|
||||
SELECT @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(lng,lat), 4326);
|
||||
$$ language SQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-- Mode
|
||||
-- https://wiki.postgresql.org/wiki/Aggregate_Mode
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_Math_final_mode(anyarray)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Math_final_mode(anyarray)
|
||||
RETURNS anyelement AS
|
||||
$BODY$
|
||||
SELECT a
|
||||
@@ -15,12 +15,12 @@ $BODY$
|
||||
$BODY$
|
||||
LANGUAGE 'sql' IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
DROP AGGREGATE IF EXISTS cartodb.CDB_Math_Mode(anyelement);
|
||||
DROP AGGREGATE IF EXISTS @extschema@.CDB_Math_Mode(anyelement);
|
||||
|
||||
CREATE AGGREGATE cartodb.CDB_Math_Mode(anyelement) (
|
||||
CREATE AGGREGATE @extschema@.CDB_Math_Mode(anyelement) (
|
||||
SFUNC=array_append,
|
||||
STYPE=anyarray,
|
||||
FINALFUNC=_CDB_Math_final_mode,
|
||||
FINALFUNC=@extschema@._CDB_Math_final_mode,
|
||||
PARALLEL = SAFE,
|
||||
INITCOND='{}'
|
||||
);
|
||||
|
||||
53
scripts-available/CDB_OAuth.sql
Normal file
53
scripts-available/CDB_OAuth.sql
Normal file
@@ -0,0 +1,53 @@
|
||||
-- Function that reassign the owner of a table to their ownership_role
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_OAuthReassignTableOwnerOnCreation()
|
||||
RETURNS event_trigger
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
obj record;
|
||||
owner_role text;
|
||||
creator_role text;
|
||||
BEGIN
|
||||
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
|
||||
LOOP
|
||||
RAISE DEBUG '% ddl object: % % % %',
|
||||
tg_tag,
|
||||
obj.command_tag,
|
||||
obj.object_type,
|
||||
obj.schema_name,
|
||||
obj.object_identity;
|
||||
SELECT rolname FROM pg_class JOIN pg_roles ON relowner = pg_roles.oid WHERE pg_class.oid = obj.objid INTO creator_role;
|
||||
SELECT value->>'ownership_role_name' from cdb_conf where key = 'api_keys_' || creator_role INTO owner_role;
|
||||
IF owner_role IS NULL OR owner_role = '' THEN
|
||||
CONTINUE;
|
||||
ELSE
|
||||
EXECUTE 'ALTER ' || obj.object_type || ' ' || obj.object_identity || ' OWNER TO ' || QUOTE_IDENT(owner_role);
|
||||
EXECUTE 'GRANT ALL ON ' || obj.object_identity || ' TO ' || QUOTE_IDENT(creator_role);
|
||||
RAISE DEBUG 'Changing ownership from % to %', creator_role, owner_role;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Creates the trigger on DDL events in order to reassign the owner
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_EnableOAuthReassignTablesTrigger()
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
DROP EVENT TRIGGER IF EXISTS oauth_reassign_tables_trigger;
|
||||
|
||||
CREATE EVENT TRIGGER oauth_reassign_tables_trigger
|
||||
ON ddl_command_end
|
||||
WHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'CREATE VIEW', 'CREATE FOREIGN TABLE', 'CREATE MATERIALIZED VIEW', 'CREATE SEQUENCE', 'CREATE FUNCTION')
|
||||
EXECUTE PROCEDURE @extschema@.CDB_OAuthReassignTableOwnerOnCreation();
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Deletes the trigger on DDL events in order to reassign the owner
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DisableOAuthReassignTablesTrigger()
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
DROP EVENT TRIGGER IF EXISTS oauth_reassign_tables_trigger;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
@@ -1,5 +1,5 @@
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Member_Group_Role_Member_Name()
|
||||
FUNCTION @extschema@.CDB_Organization_Member_Group_Role_Member_Name()
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
SELECT 'cdb_org_member'::text || '_' || md5(current_database());
|
||||
@@ -10,7 +10,7 @@ DO LANGUAGE 'plpgsql' $$
|
||||
DECLARE
|
||||
cdb_org_member_role_name TEXT;
|
||||
BEGIN
|
||||
cdb_org_member_role_name := cartodb.CDB_Organization_Member_Group_Role_Member_Name();
|
||||
cdb_org_member_role_name := @extschema@.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;';
|
||||
@@ -19,11 +19,11 @@ END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Create_Member(role_name text)
|
||||
FUNCTION @extschema@.CDB_Organization_Create_Member(role_name text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
EXECUTE 'GRANT "' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || '" TO "' || role_name || '"';
|
||||
EXECUTE 'GRANT "' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || '" TO "' || role_name || '"';
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -31,7 +31,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Administrator
|
||||
-------------------------------------------------------------------------------
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Organization_Admin_Role_Name()
|
||||
FUNCTION @extschema@._CDB_Organization_Admin_Role_Name()
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
SELECT current_database() || '_a'::text;
|
||||
@@ -43,7 +43,7 @@ DO LANGUAGE 'plpgsql' $$
|
||||
DECLARE
|
||||
cdb_org_admin_role_name TEXT;
|
||||
BEGIN
|
||||
cdb_org_admin_role_name := cartodb._CDB_Organization_Admin_Role_Name();
|
||||
cdb_org_admin_role_name := @extschema@._CDB_Organization_Admin_Role_Name();
|
||||
IF NOT EXISTS ( SELECT * FROM pg_roles WHERE rolname= cdb_org_admin_role_name )
|
||||
THEN
|
||||
EXECUTE format('CREATE ROLE %I CREATEROLE NOLOGIN;', cdb_org_admin_role_name);
|
||||
@@ -52,15 +52,15 @@ END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_AddAdmin(username text)
|
||||
FUNCTION @extschema@.CDB_Organization_AddAdmin(username text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
cdb_user_role TEXT;
|
||||
cdb_admin_role TEXT;
|
||||
BEGIN
|
||||
cdb_admin_role := cartodb._CDB_Organization_Admin_Role_Name();
|
||||
cdb_user_role := cartodb._CDB_User_RoleFromUsername(username);
|
||||
cdb_admin_role := @extschema@._CDB_Organization_Admin_Role_Name();
|
||||
cdb_user_role := @extschema@._CDB_User_RoleFromUsername(username);
|
||||
EXECUTE format('GRANT %I TO %I WITH ADMIN OPTION', cdb_admin_role, cdb_user_role);
|
||||
-- CREATEROLE is not inherited, and is needed for user creation
|
||||
EXECUTE format('ALTER ROLE %I CREATEROLE', cdb_user_role);
|
||||
@@ -68,15 +68,15 @@ END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_RemoveAdmin(username text)
|
||||
FUNCTION @extschema@.CDB_Organization_RemoveAdmin(username text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
cdb_user_role TEXT;
|
||||
cdb_admin_role TEXT;
|
||||
BEGIN
|
||||
cdb_admin_role := cartodb._CDB_Organization_Admin_Role_Name();
|
||||
cdb_user_role := cartodb._CDB_User_RoleFromUsername(username);
|
||||
cdb_admin_role := @extschema@._CDB_Organization_Admin_Role_Name();
|
||||
cdb_user_role := @extschema@._CDB_User_RoleFromUsername(username);
|
||||
EXECUTE format('ALTER ROLE %I NOCREATEROLE', cdb_user_role);
|
||||
EXECUTE format('REVOKE %I FROM %I', cdb_admin_role, cdb_user_role);
|
||||
END
|
||||
@@ -86,7 +86,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Sharing tables
|
||||
-------------------------------------------------------------------------------
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Permission(from_schema text, table_name text, to_role_name text)
|
||||
FUNCTION @extschema@.CDB_Organization_Add_Table_Read_Permission(from_schema text, table_name text, to_role_name text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -96,21 +96,21 @@ END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Permission(from_schema text, table_name text)
|
||||
FUNCTION @extschema@.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() || ''');';
|
||||
EXECUTE 'SELECT @extschema@.CDB_Organization_Add_Table_Read_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb._CDB_Organization_Get_Table_Sequences(from_schema text, table_name text)
|
||||
FUNCTION @extschema@._CDB_Organization_Get_Table_Sequences(from_schema text, table_name text)
|
||||
RETURNS SETOF TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY EXECUTE 'SELECT
|
||||
n.nspname || ''.'' || c.relname
|
||||
quote_ident(n.nspname) || ''.'' || quote_ident(c.relname)
|
||||
FROM
|
||||
pg_depend d
|
||||
JOIN pg_class c ON d.objid = c.oid
|
||||
@@ -124,7 +124,7 @@ END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Write_Permission(from_schema text, table_name text, to_role_name text)
|
||||
FUNCTION @extschema@.CDB_Organization_Add_Table_Read_Write_Permission(from_schema text, table_name text, to_role_name text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -133,24 +133,24 @@ 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 || '"';
|
||||
|
||||
FOR sequence_name IN SELECT * FROM cartodb._CDB_Organization_Get_Table_Sequences(from_schema, table_name) LOOP
|
||||
FOR sequence_name IN SELECT * FROM @extschema@._CDB_Organization_Get_Table_Sequences(from_schema, table_name) LOOP
|
||||
EXECUTE 'GRANT USAGE, SELECT ON SEQUENCE ' || sequence_name || ' TO "' || to_role_name || '"';
|
||||
END LOOP;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Write_Permission(from_schema text, table_name text)
|
||||
FUNCTION @extschema@.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() || ''');';
|
||||
EXECUTE 'SELECT @extschema@.CDB_Organization_Add_Table_Read_Write_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Remove_Access_Permission(from_schema text, table_name text, to_role_name text)
|
||||
FUNCTION @extschema@.CDB_Organization_Remove_Access_Permission(from_schema text, table_name text, to_role_name text)
|
||||
RETURNS void
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -162,10 +162,37 @@ END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION cartodb.CDB_Organization_Remove_Organization_Access_Permission(from_schema text, table_name text)
|
||||
FUNCTION @extschema@.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() || ''');';
|
||||
EXECUTE 'SELECT @extschema@.CDB_Organization_Remove_Access_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Role management
|
||||
--------------------------------------------------------------------------------
|
||||
CREATE OR REPLACE
|
||||
FUNCTION @extschema@.CDB_Organization_Grant_Role(role_name name)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
org_role TEXT;
|
||||
BEGIN
|
||||
org_role := @extschema@.CDB_Organization_Member_Group_Role_Member_Name();
|
||||
EXECUTE format('GRANT %I TO %I', role_name, org_role);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE
|
||||
FUNCTION @extschema@.CDB_Organization_Revoke_Role(role_name name)
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
org_role TEXT;
|
||||
BEGIN
|
||||
org_role := @extschema@.CDB_Organization_Member_Group_Role_Member_Name();
|
||||
EXECUTE format('REVOKE %I FROM %I', role_name, org_role);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
-- Scope: public
|
||||
-- Parameters:
|
||||
-- reloid: oid of the table.
|
||||
CREATE OR REPLACE FUNCTION CDB_DropOverviews(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_DropOverviews(reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -10,9 +10,9 @@ DECLARE
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
FOR row IN
|
||||
SELECT * FROM CDB_Overviews(reloid)
|
||||
SELECT * FROM @extschema@.CDB_Overviews(reloid)
|
||||
LOOP
|
||||
EXECUTE Format('DROP TABLE %s;', row.overview_table);
|
||||
RAISE NOTICE 'Dropped overview for level %: %', row.z, row.overview_table;
|
||||
@@ -29,20 +29,20 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Return relation of overviews for the table with
|
||||
-- the base table oid,
|
||||
-- z level of the overview and overview table oid, ordered by z.
|
||||
CREATE OR REPLACE FUNCTION CDB_Overviews(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Overviews(reloid REGCLASS)
|
||||
RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
||||
AS $$
|
||||
DECLARE
|
||||
schema_name TEXT;
|
||||
base_table_name TEXT;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, base_table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, base_table_name;
|
||||
RETURN QUERY SELECT
|
||||
reloid AS base_table,
|
||||
_CDB_OverviewTableZ(table_name) AS z,
|
||||
@extschema@._CDB_OverviewTableZ(table_name) AS z,
|
||||
table_regclass AS overview_table
|
||||
FROM _CDB_UserTablesInSchema(schema_name)
|
||||
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=reloid), table_name)
|
||||
FROM @extschema@._CDB_UserTablesInSchema(schema_name)
|
||||
WHERE @extschema@._CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=reloid), table_name)
|
||||
ORDER BY z;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL STABLE PARALLEL RESTRICTED;
|
||||
@@ -56,18 +56,18 @@ $$ LANGUAGE PLPGSQL STABLE PARALLEL RESTRICTED;
|
||||
-- z level of the overview and overview table oid, ordered by z.
|
||||
-- Note: CDB_Overviews can be applied to the result of CDB_QueryTablesText
|
||||
-- to obtain the overviews applicable to a query.
|
||||
CREATE OR REPLACE FUNCTION CDB_Overviews(tables regclass[])
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Overviews(tables regclass[])
|
||||
RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
||||
AS $$
|
||||
SELECT
|
||||
base_table::regclass AS base_table,
|
||||
_CDB_OverviewTableZ(table_name) AS z,
|
||||
@extschema@._CDB_OverviewTableZ(table_name) AS z,
|
||||
table_regclass AS overview_table
|
||||
FROM
|
||||
_CDB_UserTablesInSchema(), unnest(tables) base_table
|
||||
@extschema@._CDB_UserTablesInSchema(), unnest(tables) base_table
|
||||
WHERE
|
||||
schema_name = _cdb_schema_name(base_table)
|
||||
AND _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=base_table), table_name)
|
||||
schema_name = @extschema@._cdb_schema_name(base_table)
|
||||
AND @extschema@._CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=base_table), table_name)
|
||||
ORDER BY base_table, z;
|
||||
$$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
|
||||
@@ -76,11 +76,11 @@ $$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
-- Parameters
|
||||
-- reloid: oid of the input table.
|
||||
-- Return value A box2d extent in 3857.
|
||||
CREATE OR REPLACE FUNCTION _cdb_estimated_extent(reloid REGCLASS)
|
||||
RETURNS box2d
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_estimated_extent(reloid REGCLASS)
|
||||
RETURNS @postgisschema@.box2d
|
||||
AS $$
|
||||
DECLARE
|
||||
ext box2d;
|
||||
ext @postgisschema@.box2d;
|
||||
ext_query text;
|
||||
table_id record;
|
||||
BEGIN
|
||||
@@ -89,25 +89,22 @@ AS $$
|
||||
FROM pg_class c JOIN pg_namespace n on n.oid = c.relnamespace WHERE c.oid = reloid::oid;
|
||||
|
||||
ext_query = format(
|
||||
'SELECT ST_EstimatedExtent(''%1$s'', ''%2$s'', ''%3$s'');',
|
||||
'SELECT @postgisschema@.ST_EstimatedExtent(''%1$s'', ''%2$s'', ''%3$s'');',
|
||||
table_id.schema_name, table_id.table_name, 'the_geom_webmercator'
|
||||
);
|
||||
|
||||
BEGIN
|
||||
EXECUTE ext_query INTO ext;
|
||||
EXCEPTION
|
||||
-- This is the typical ERROR: stats for "mytable" do not exist
|
||||
WHEN internal_error THEN
|
||||
EXECUTE ext_query INTO ext;
|
||||
IF ext IS NULL THEN
|
||||
-- Get stats and execute again
|
||||
EXECUTE format('ANALYZE %1$s', reloid);
|
||||
|
||||
-- We check the geometry type in case the error is due to empty geometries
|
||||
IF _CDB_GeometryTypes(reloid) IS NULL THEN
|
||||
IF @extschema@._CDB_GeometryTypes(reloid) IS NULL THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
EXECUTE ext_query INTO ext;
|
||||
END;
|
||||
END IF;
|
||||
|
||||
RETURN ext;
|
||||
END;
|
||||
@@ -119,7 +116,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- nz: number of zoom levels to consider from z0 upward.
|
||||
-- Return value: feature density (num_features / webmercator_squared_meters).
|
||||
CREATE OR REPLACE FUNCTION _CDB_Feature_Density(reloid REGCLASS, nz integer)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Feature_Density(reloid REGCLASS, nz integer)
|
||||
RETURNS FLOAT8
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -138,7 +135,7 @@ AS $$
|
||||
-- the area of tiles at level Z: c*c*power(2, -2*z)
|
||||
-- with c = CDB_XYZ_Resolution(-8) (earth circumference)
|
||||
min_features = '500';
|
||||
SELECT CDB_XYZ_Resolution(-8) INTO c;
|
||||
SELECT @extschema@.CDB_XYZ_Resolution(-8) INTO c;
|
||||
|
||||
-- We first compute a set of *seed* tiles, of the minimum Z level, z0, such that
|
||||
-- they cover the extent of the table and we have at least n of them in each
|
||||
@@ -149,27 +146,27 @@ AS $$
|
||||
-- considered tiles.
|
||||
EXECUTE Format('
|
||||
WITH RECURSIVE t(x, y, z, e) AS (
|
||||
WITH ext AS (SELECT _cdb_estimated_extent(%6$s) as g),
|
||||
WITH ext AS (SELECT @extschema@._cdb_estimated_extent(%6$s) as g),
|
||||
base AS (
|
||||
SELECT
|
||||
least(
|
||||
-floor(log(2, (greatest(ST_XMax(ext.g)-ST_XMin(ext.g), ST_YMax(ext.g)-ST_YMin(ext.g))/(%4$s*%5$s))::numeric)),
|
||||
_CDB_MaxOverviewLevel()+1
|
||||
-floor(log(2, (greatest(@postgisschema@.ST_XMax(ext.g)-@postgisschema@.ST_XMin(ext.g), @postgisschema@.ST_YMax(ext.g)-@postgisschema@.ST_YMin(ext.g))/(%4$s*%5$s))::numeric)),
|
||||
@extschema@._CDB_MaxOverviewLevel()+1
|
||||
)::integer z
|
||||
FROM ext
|
||||
),
|
||||
lim AS (
|
||||
SELECT
|
||||
FLOOR((ST_XMin(ext.g)+CDB_XYZ_Resolution(0)*128)/(CDB_XYZ_Resolution(base.z)*256))::integer x0,
|
||||
FLOOR((ST_XMax(ext.g)+CDB_XYZ_Resolution(0)*128)/(CDB_XYZ_Resolution(base.z)*256))::integer x1,
|
||||
FLOOR((CDB_XYZ_Resolution(0)*128-ST_YMin(ext.g))/(CDB_XYZ_Resolution(base.z)*256))::integer y1,
|
||||
FLOOR((CDB_XYZ_Resolution(0)*128-ST_YMax(ext.g))/(CDB_XYZ_Resolution(base.z)*256))::integer y0
|
||||
FLOOR((@postgisschema@.ST_XMin(ext.g)+@extschema@.CDB_XYZ_Resolution(0)*128)/(@extschema@.CDB_XYZ_Resolution(base.z)*256))::integer x0,
|
||||
FLOOR((@postgisschema@.ST_XMax(ext.g)+@extschema@.CDB_XYZ_Resolution(0)*128)/(@extschema@.CDB_XYZ_Resolution(base.z)*256))::integer x1,
|
||||
FLOOR((@extschema@.CDB_XYZ_Resolution(0)*128-@postgisschema@.ST_YMin(ext.g))/(@extschema@.CDB_XYZ_Resolution(base.z)*256))::integer y1,
|
||||
FLOOR((@extschema@.CDB_XYZ_Resolution(0)*128-@postgisschema@.ST_YMax(ext.g))/(@extschema@.CDB_XYZ_Resolution(base.z)*256))::integer y0
|
||||
FROM ext, base
|
||||
),
|
||||
seed AS (
|
||||
SELECT xt, yt, base.z, (
|
||||
SELECT count(*) FROM %1$s
|
||||
WHERE the_geom_webmercator && CDB_XYZ_Extent(xt, yt, base.z)
|
||||
WHERE the_geom_webmercator && @extschema@.CDB_XYZ_Extent(xt, yt, base.z)
|
||||
) e
|
||||
FROM base, lim, generate_series(lim.x0, lim.x1) xt, generate_series(lim.y0, lim.y1) yt
|
||||
)
|
||||
@@ -177,12 +174,12 @@ AS $$
|
||||
UNION ALL
|
||||
SELECT x*2 + xx, y*2 + yy, t.z+1, (
|
||||
SELECT count(*) FROM %1$s
|
||||
WHERE the_geom_webmercator && CDB_XYZ_Extent(t.x*2 + c.xx, t.y*2 + c.yy, t.z+1)
|
||||
WHERE the_geom_webmercator && @extschema@.CDB_XYZ_Extent(t.x*2 + c.xx, t.y*2 + c.yy, t.z+1)
|
||||
)
|
||||
FROM t, base, (VALUES (0, 0), (0, 1), (1, 1), (1, 0)) AS c(xx, yy)
|
||||
WHERE t.e > %2$s AND t.z < least(base.z + %3$s, _CDB_MaxZoomLevel())
|
||||
)
|
||||
SELECT MAX(e/ST_Area(CDB_XYZ_Extent(x,y,z))) FROM t where e > 0;
|
||||
SELECT MAX(e/@postgisschema@.ST_Area(@extschema@.CDB_XYZ_Extent(x,y,z))) FROM t where e > 0;
|
||||
', reloid::text, min_features, nz, n, c, reloid::oid)
|
||||
INTO fd;
|
||||
RETURN fd;
|
||||
@@ -196,7 +193,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Parameters:
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- Return value: Z level as an integer
|
||||
CREATE OR REPLACE FUNCTION _CDB_Feature_Density_Ref_Z_Strategy(reloid REGCLASS, tolerance_px FLOAT8 DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Feature_Density_Ref_Z_Strategy(reloid REGCLASS, tolerance_px FLOAT8 DEFAULT NULL)
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -213,15 +210,15 @@ AS $$
|
||||
|
||||
-- Compute fd as an estimation of the (maximum) number
|
||||
-- of features per unit of tile area (in webmercator squared meters)
|
||||
SELECT _CDB_Feature_Density(reloid, nz) INTO fd;
|
||||
SELECT @extschema@._CDB_Feature_Density(reloid, nz) INTO fd;
|
||||
-- lim maximum number of (desiderable) features per tile
|
||||
-- we have c = 2*Pi*R = CDB_XYZ_Resolution(-8) (earth circumference)
|
||||
-- ta(z): tile area = power(c*power(2,-z), 2) = c*c*power(2,-2*z)
|
||||
-- => fd*ta(z) is the average number of features per tile at level z
|
||||
-- find minimum z so that fd*ta(z) <= lim
|
||||
-- compute a rough 'feature density' value
|
||||
SELECT CDB_XYZ_Resolution(-8) INTO c;
|
||||
RETURN least(_CDB_MaxOverviewLevel()+1, ceil(log(2.0, (c*c*fd/lim)::numeric)/2));
|
||||
SELECT @extschema@.CDB_XYZ_Resolution(-8) INTO c;
|
||||
RETURN least(@extschema@._CDB_MaxOverviewLevel()+1, ceil(log(2.0, (c*c*fd/lim)::numeric)/2));
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
@@ -234,7 +231,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- overview_z Z level of the overview to be named, must be smaller than ref_z
|
||||
-- Return value: the name to be used for the overview. The name is always
|
||||
-- unqualified (does not include a schema name).
|
||||
CREATE OR REPLACE FUNCTION _CDB_Overview_Name(ref REGCLASS, ref_z INTEGER, overview_z INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Overview_Name(ref REGCLASS, ref_z INTEGER, overview_z INTEGER)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -243,9 +240,9 @@ AS $$
|
||||
suffix TEXT;
|
||||
is_overview BOOLEAN;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(ref) INTO schema_name, base;
|
||||
SELECT _CDB_OverviewBaseTableName(base) INTO base;
|
||||
RETURN _CDB_OverviewTableName(base, overview_z);
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(ref) INTO schema_name, base;
|
||||
SELECT @extschema@._CDB_OverviewBaseTableName(base) INTO base;
|
||||
RETURN @extschema@._CDB_OverviewTableName(base, overview_z);
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
@@ -257,7 +254,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- ref_z Z level assigned to the original table
|
||||
-- overview_z Z level of the overview to be generated, must be smaller than ref_z
|
||||
-- Return value: Name of the generated overview table
|
||||
CREATE OR REPLACE FUNCTION _CDB_Sampling_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, tolerance_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Sampling_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, tolerance_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -271,11 +268,11 @@ AS $$
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
overview_rel := @extschema@._CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
-- TODO: compute fraction from tolerance_px if not NULL
|
||||
fraction := power(2, 2*(overview_z - ref_z));
|
||||
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
|
||||
overview_table_name := Format('%I.%I', schema_name, overview_rel);
|
||||
IF has_overview_created THEN
|
||||
@@ -302,7 +299,7 @@ AS $$
|
||||
%1$s SELECT * FROM %2$s
|
||||
WHERE ctid = ANY (
|
||||
ARRAY[
|
||||
(SELECT CDB_RandomTids(''%2$s'', %3$s))
|
||||
(SELECT @extschema@.CDB_RandomTids(''%2$s'', %3$s))
|
||||
]
|
||||
);
|
||||
', creation_clause, reloid, num_samples);
|
||||
@@ -323,7 +320,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- overview table to match those of the dataset. It will only perform any change
|
||||
-- if the overview table belgons to the same scheme as the dataset and it
|
||||
-- matches the scheme naming for overview tables.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Register_Overview(dataset REGCLASS, overview_table REGCLASS, overview_z INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Register_Overview(dataset REGCLASS, overview_table REGCLASS, overview_z INTEGER)
|
||||
RETURNS VOID
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -337,10 +334,10 @@ AS $$
|
||||
-- This function will only register a table as an overview table if it matches
|
||||
-- the overviews naming scheme for the dataset and z level and the table belongs
|
||||
-- to the same scheme as the the dataset
|
||||
SELECT * FROM _cdb_split_table_name(dataset) INTO dataset_scheme, dataset_name;
|
||||
SELECT * FROM _cdb_split_table_name(overview_table) INTO overview_scheme, overview_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(dataset) INTO dataset_scheme, dataset_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(overview_table) INTO overview_scheme, overview_name;
|
||||
IF dataset_scheme = overview_scheme AND
|
||||
overview_name = _CDB_OverviewTableName(dataset_name, overview_z) THEN
|
||||
overview_name = @extschema@._CDB_OverviewTableName(dataset_name, overview_z) THEN
|
||||
|
||||
-- preserve the owner of the base table
|
||||
SELECT u.usename
|
||||
@@ -359,7 +356,7 @@ AS $$
|
||||
WHERE c_from.oid = dataset
|
||||
AND c_to.oid = overview_table;
|
||||
|
||||
PERFORM _CDB_Add_Indexes(overview_table);
|
||||
PERFORM @extschema@._CDB_Add_Indexes(overview_table);
|
||||
|
||||
-- TODO: If metadata about existing overviews is to be stored
|
||||
-- it should be done here (CDB_Overviews would consume such metadata)
|
||||
@@ -374,10 +371,10 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
-- Parameters
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- Return value: set of attribute names
|
||||
CREATE OR REPLACE FUNCTION _CDB_Aggregable_Attributes(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Aggregable_Attributes(reloid REGCLASS)
|
||||
RETURNS SETOF information_schema.sql_identifier
|
||||
AS $$
|
||||
SELECT c FROM CDB_ColumnNames(reloid) c, _CDB_Columns() cdb
|
||||
SELECT c FROM @extschema@.CDB_ColumnNames(reloid) c, @extschema@._CDB_Columns() cdb
|
||||
WHERE c NOT IN (
|
||||
cdb.pkey, cdb.geomcol, cdb.mercgeomcol
|
||||
)
|
||||
@@ -389,14 +386,14 @@ $$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
-- Parameters
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- Return value: SQL subexpression as text
|
||||
CREATE OR REPLACE FUNCTION _CDB_Aggregable_Attributes_Expression(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Aggregable_Attributes_Expression(reloid REGCLASS)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
attr_list TEXT;
|
||||
BEGIN
|
||||
SELECT string_agg(s.c, ',') FROM (
|
||||
SELECT * FROM _CDB_Aggregable_Attributes(reloid) c
|
||||
SELECT * FROM @extschema@._CDB_Aggregable_Attributes(reloid) c
|
||||
) AS s INTO attr_list;
|
||||
|
||||
RETURN attr_list;
|
||||
@@ -404,7 +401,7 @@ END
|
||||
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- Check if a column of a table is of an unlimited-length text type
|
||||
CREATE OR REPLACE FUNCTION _cdb_unlimited_text_column(reloid REGCLASS, col_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_unlimited_text_column(reloid REGCLASS, col_name TEXT)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
@@ -419,7 +416,7 @@ AS $$
|
||||
);
|
||||
$$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _cdb_categorical_column(reloid REGCLASS, col_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_categorical_column(reloid REGCLASS, col_name TEXT)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -448,7 +445,7 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL RESTRICTED;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _cdb_mode_of_array(anyarray)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_mode_of_array(anyarray)
|
||||
RETURNS anyelement AS
|
||||
$$
|
||||
SELECT a
|
||||
@@ -459,11 +456,11 @@ $$
|
||||
$$
|
||||
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
DROP AGGREGATE IF EXISTS _cdb_mode(anyelement);
|
||||
CREATE AGGREGATE _cdb_mode(anyelement) (
|
||||
DROP AGGREGATE IF EXISTS @extschema@._cdb_mode(anyelement);
|
||||
CREATE AGGREGATE @extschema@._cdb_mode(anyelement) (
|
||||
SFUNC=array_append,
|
||||
STYPE=anyarray,
|
||||
FINALFUNC=_cdb_mode_of_array,
|
||||
FINALFUNC=@extschema@._cdb_mode_of_array,
|
||||
PARALLEL = SAFE,
|
||||
INITCOND='{}'
|
||||
);
|
||||
@@ -476,7 +473,7 @@ CREATE AGGREGATE _cdb_mode(anyelement) (
|
||||
-- table_alias: (optional) table qualifier for the column to be aggregated
|
||||
-- Return SQL subexpression as text with aggregated attribute aliased
|
||||
-- with its original name.
|
||||
CREATE OR REPLACE FUNCTION _CDB_Attribute_Aggregation_Expression(reloid REGCLASS, column_name TEXT, table_alias TEXT DEFAULT '')
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Attribute_Aggregation_Expression(reloid REGCLASS, column_name TEXT, table_alias TEXT DEFAULT '')
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -493,10 +490,10 @@ BEGIN
|
||||
qualified_column := Format('%I', column_name);
|
||||
END IF;
|
||||
|
||||
column_type := CDB_ColumnType(reloid, column_name);
|
||||
column_type := @extschema@.CDB_ColumnType(reloid, column_name);
|
||||
|
||||
SELECT EXISTS (
|
||||
SELECT * FROM CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
||||
SELECT * FROM @extschema@.CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
||||
) INTO has_counter_column;
|
||||
IF has_counter_column THEN
|
||||
feature_count := '_feature_count';
|
||||
@@ -506,24 +503,24 @@ BEGIN
|
||||
total_feature_count := 'count(*)';
|
||||
END IF;
|
||||
|
||||
base_table := _CDB_OverviewBaseTable(reloid);
|
||||
base_table := @extschema@._CDB_OverviewBaseTable(reloid);
|
||||
|
||||
CASE column_type
|
||||
WHEN 'double precision', 'real', 'integer', 'bigint', 'numeric' THEN
|
||||
IF column_name = '_feature_count' THEN
|
||||
RETURN 'SUM(_feature_count)';
|
||||
ELSE
|
||||
IF column_type = 'integer' AND _cdb_categorical_column(base_table, column_name) THEN
|
||||
IF column_type = 'integer' AND @extschema@._cdb_categorical_column(base_table, column_name) THEN
|
||||
RETURN Format('CDB_Math_Mode(%s)::', qualified_column) || column_type;
|
||||
ELSE
|
||||
RETURN Format('SUM(%s*%s)/%s::' || column_type, qualified_column, feature_count, total_feature_count);
|
||||
END IF;
|
||||
END IF;
|
||||
WHEN 'text', 'character varying', 'character' THEN
|
||||
IF _cdb_categorical_column(base_table, column_name) THEN
|
||||
IF @extschema@._cdb_categorical_column(base_table, column_name) THEN
|
||||
RETURN Format('_cdb_mode(%s)::', qualified_column) || column_type;
|
||||
ELSE
|
||||
IF _cdb_unlimited_text_column(base_table, column_name) THEN
|
||||
IF @extschema@._cdb_unlimited_text_column(base_table, column_name) THEN
|
||||
-- TODO: this should not be applied to columns containing largish text;
|
||||
-- it is intended only to short names/identifiers
|
||||
RETURN 'CASE WHEN count(distinct ' || qualified_column || ') = 1 THEN MIN(' || qualified_column || ') WHEN ' || total_feature_count || ' < 5 THEN string_agg(distinct ' || qualified_column || ','' / '') ELSE ''*'' END::' || column_type;
|
||||
@@ -545,15 +542,15 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL RESTRICTED;
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- table_alias: (optional) table qualifier for the columns to be aggregated
|
||||
-- Return value: SQL subexpression as text
|
||||
CREATE OR REPLACE FUNCTION _CDB_Aggregated_Attributes_Expression(reloid REGCLASS, table_alias TEXT DEFAULT '')
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Aggregated_Attributes_Expression(reloid REGCLASS, table_alias TEXT DEFAULT '')
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
attr_list TEXT;
|
||||
BEGIN
|
||||
SELECT string_agg(_CDB_Attribute_Aggregation_Expression(reloid, s.c, table_alias) || Format(' AS %s', s.c), ',')
|
||||
SELECT string_agg(@extschema@._CDB_Attribute_Aggregation_Expression(reloid, s.c, table_alias) || Format(' AS %s', s.c), ',')
|
||||
FROM (
|
||||
SELECT * FROM _CDB_Aggregable_Attributes(reloid) c
|
||||
SELECT * FROM @extschema@._CDB_Aggregable_Attributes(reloid) c
|
||||
) AS s INTO attr_list;
|
||||
|
||||
RETURN attr_list;
|
||||
@@ -565,14 +562,14 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL RESTRICTED;
|
||||
-- Parameters
|
||||
-- reloid: oid of the input table. It must be a cartodbfy'ed table.
|
||||
-- Return value: array of geometry type names
|
||||
CREATE OR REPLACE FUNCTION _CDB_GeometryTypes(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GeometryTypes(reloid REGCLASS)
|
||||
RETURNS TEXT[]
|
||||
AS $$
|
||||
DECLARE
|
||||
gtypes TEXT[];
|
||||
BEGIN
|
||||
EXECUTE Format('
|
||||
SELECT array_agg(DISTINCT ST_GeometryType(the_geom)) FROM (
|
||||
SELECT array_agg(DISTINCT @postgisschema@.ST_GeometryType(the_geom)) FROM (
|
||||
SELECT the_geom FROM %s
|
||||
WHERE (the_geom is not null) LIMIT 10
|
||||
) as geom_types
|
||||
@@ -592,7 +589,7 @@ $$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
|
||||
-- ref_z Z level assigned to the original table
|
||||
-- overview_z Z level of the overview to be generated, must be smaller than ref_z
|
||||
-- Return value: Name of the generated overview table
|
||||
CREATE OR REPLACE FUNCTION _CDB_GridCluster_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GridCluster_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -616,7 +613,7 @@ AS $$
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
SELECT @extschema@._CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
IF gtypes IS NULL OR array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
||||
-- This strategy only supports datasets with point geomety
|
||||
RETURN NULL;
|
||||
@@ -624,22 +621,22 @@ AS $$
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
overview_rel := @extschema@._CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
|
||||
-- Grid size in pixels at Z level overview_z
|
||||
IF grid_px IS NULL THEN
|
||||
grid_px := 1.0;
|
||||
END IF;
|
||||
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
SELECT @extschema@.CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
-- grid size in meters
|
||||
grid_m = grid_px * pixel_m;
|
||||
|
||||
attributes := _CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := _CDB_Aggregated_Attributes_Expression(reloid);
|
||||
attributes := @extschema@._CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := @extschema@._CDB_Aggregated_Attributes_Expression(reloid);
|
||||
IF attributes <> '' THEN
|
||||
attributes := ', ' || attributes;
|
||||
END IF;
|
||||
@@ -669,12 +666,12 @@ AS $$
|
||||
CASE c
|
||||
WHEN 'cartodb_id' THEN 'cartodb_id'
|
||||
WHEN 'the_geom' THEN
|
||||
Format('ST_Transform(%s, 4326) AS the_geom', point_geom)
|
||||
Format('@postgisschema@.ST_Transform(%s, 4326) AS the_geom', point_geom)
|
||||
WHEN 'the_geom_webmercator' THEN
|
||||
Format('%s AS the_geom_webmercator', point_geom)
|
||||
ELSE c
|
||||
END AS column
|
||||
FROM CDB_ColumnNames(reloid) c
|
||||
FROM @extschema@.CDB_ColumnNames(reloid) c
|
||||
)
|
||||
SELECT string_agg(s.column, ',') FROM (
|
||||
SELECT * FROM cols
|
||||
@@ -704,8 +701,8 @@ AS $$
|
||||
SELECT
|
||||
%5$s
|
||||
count(*) AS n,
|
||||
Floor(ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
Floor(ST_Y(f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
Floor(@postgisschema@.ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
Floor(@postgisschema@.ST_Y(f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
MIN(cartodb_id) AS cartodb_id
|
||||
FROM %1$s f
|
||||
WHERE f.the_geom_webmercator IS NOT NULL
|
||||
@@ -719,7 +716,7 @@ AS $$
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- This strategy places the aggregation of each cluster at the centroid of the cluster members.
|
||||
CREATE OR REPLACE FUNCTION _CDB_GridClusterCentroid_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GridClusterCentroid_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -743,7 +740,7 @@ AS $$
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
SELECT @extschema@._CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
IF gtypes IS NULL OR array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
||||
-- This strategy only supports datasets with point geomety
|
||||
RETURN NULL;
|
||||
@@ -751,22 +748,22 @@ AS $$
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
overview_rel := @extschema@._CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
|
||||
-- Grid size in pixels at Z level overview_z
|
||||
IF grid_px IS NULL THEN
|
||||
grid_px := 1.0;
|
||||
END IF;
|
||||
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
SELECT @extschema@.CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
-- grid size in meters
|
||||
grid_m = grid_px * pixel_m;
|
||||
|
||||
attributes := _CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := _CDB_Aggregated_Attributes_Expression(reloid);
|
||||
attributes := @extschema@._CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := @extschema@._CDB_Aggregated_Attributes_Expression(reloid);
|
||||
IF attributes <> '' THEN
|
||||
attributes := ', ' || attributes;
|
||||
END IF;
|
||||
@@ -788,7 +785,7 @@ AS $$
|
||||
offset_y := Format('%2$s/2 - MOD((%1$s)::numeric, (%2$s)::numeric)::float8', cell_y, pixel_m);
|
||||
END IF;
|
||||
|
||||
point_geom := Format('ST_SetSRID(ST_MakePoint(%1$s + %3$s, %2$s + %4$s), 3857)', cell_x, cell_y, offset_x, offset_y);
|
||||
point_geom := Format('@postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(%1$s + %3$s, %2$s + %4$s), 3857)', cell_x, cell_y, offset_x, offset_y);
|
||||
|
||||
-- compute the resulting columns in the same order as in the base table
|
||||
WITH cols AS (
|
||||
@@ -796,9 +793,9 @@ AS $$
|
||||
CASE c
|
||||
WHEN 'cartodb_id' THEN 'cartodb_id'
|
||||
WHEN 'the_geom' THEN
|
||||
'ST_Transform(ST_SetSRID(ST_MakePoint(_sum_of_x/n, _sum_of_y/n), 3857), 4326) AS the_geom'
|
||||
'@postgisschema@.ST_Transform(@postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(_sum_of_x/n, _sum_of_y/n), 3857), 4326) AS the_geom'
|
||||
WHEN 'the_geom_webmercator' THEN
|
||||
'ST_SetSRID(ST_MakePoint(_sum_of_x/n, _sum_of_y/n), 3857) AS the_geom_webmercator'
|
||||
'@postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(_sum_of_x/n, _sum_of_y/n), 3857) AS the_geom_webmercator'
|
||||
ELSE c
|
||||
END AS column
|
||||
FROM CDB_ColumnNames(reloid) c
|
||||
@@ -831,10 +828,10 @@ AS $$
|
||||
SELECT
|
||||
%5$s
|
||||
count(*) AS n,
|
||||
SUM(ST_X(f.the_geom_webmercator)) AS _sum_of_x,
|
||||
SUM(ST_Y(f.the_geom_webmercator)) AS _sum_of_y,
|
||||
Floor(ST_Y(f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
Floor(ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
SUM(@postgisschema@.ST_X(f.the_geom_webmercator)) AS _sum_of_x,
|
||||
SUM(@postgisschema@.ST_Y(f.the_geom_webmercator)) AS _sum_of_y,
|
||||
Floor(@postgisschema@.ST_Y(f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
Floor(@postgisschema@.ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
MIN(cartodb_id) AS cartodb_id
|
||||
FROM %1$s f
|
||||
GROUP BY gx, gy
|
||||
@@ -847,7 +844,7 @@ AS $$
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- This strategy places the aggregation of each cluster at the position of one of the cluster members.
|
||||
CREATE OR REPLACE FUNCTION _CDB_GridClusterSample_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GridClusterSample_Reduce_Strategy(reloid REGCLASS, ref_z INTEGER, overview_z INTEGER, grid_px FLOAT8 DEFAULT NULL, has_overview_created BOOLEAN DEFAULT FALSE)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -871,7 +868,7 @@ AS $$
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
SELECT @extschema@._CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
IF gtypes IS NULL OR array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
||||
-- This strategy only supports datasets with point geomety
|
||||
RETURN NULL;
|
||||
@@ -879,22 +876,22 @@ AS $$
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
overview_rel := @extschema@._CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
|
||||
-- Grid size in pixels at Z level overview_z
|
||||
IF grid_px IS NULL THEN
|
||||
grid_px := 1.0;
|
||||
END IF;
|
||||
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
SELECT @extschema@.CDB_XYZ_Resolution(overview_z) INTO pixel_m;
|
||||
-- grid size in meters
|
||||
grid_m = grid_px * pixel_m;
|
||||
|
||||
attributes := _CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := _CDB_Aggregated_Attributes_Expression(reloid);
|
||||
attributes := @extschema@._CDB_Aggregable_Attributes_Expression(reloid);
|
||||
aggr_attributes := @extschema@._CDB_Aggregated_Attributes_Expression(reloid);
|
||||
IF attributes <> '' THEN
|
||||
attributes := ', ' || attributes;
|
||||
END IF;
|
||||
@@ -916,7 +913,7 @@ AS $$
|
||||
offset_y := Format('%2$s/2 - MOD((%1$s)::numeric, (%2$s)::numeric)::float8', cell_y, pixel_m);
|
||||
END IF;
|
||||
|
||||
point_geom := Format('ST_SetSRID(ST_MakePoint(%1$s + %3$s, %2$s + %4$s), 3857)', cell_x, cell_y, offset_x, offset_y);
|
||||
point_geom := Format('@postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(%1$s + %3$s, %2$s + %4$s), 3857)', cell_x, cell_y, offset_x, offset_y);
|
||||
|
||||
-- compute the resulting columns in the same order as in the base table
|
||||
WITH cols AS (
|
||||
@@ -925,7 +922,7 @@ AS $$
|
||||
WHEN 'cartodb_id' THEN 'cartodb_id'
|
||||
ELSE c
|
||||
END AS column
|
||||
FROM CDB_ColumnNames(reloid) c
|
||||
FROM @extschema@.CDB_ColumnNames(reloid) c
|
||||
)
|
||||
SELECT string_agg(s.column, ',') FROM (
|
||||
SELECT * FROM cols
|
||||
@@ -955,8 +952,8 @@ AS $$
|
||||
SELECT
|
||||
%5$s
|
||||
count(*) AS n,
|
||||
Floor(ST_X(_f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
Floor(ST_Y(_f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
Floor(@postgisschema@.ST_X(_f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
Floor(@postgisschema@.ST_Y(_f.the_geom_webmercator)/%2$s)::int AS gy,
|
||||
MIN(cartodb_id) AS cartodb_id
|
||||
FROM %1$s _f
|
||||
GROUP BY gx, gy
|
||||
@@ -983,7 +980,7 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- created by the strategy must have the same columns
|
||||
-- as the base table and in the same order.
|
||||
-- Return value: Array with the names of the generated overview tables
|
||||
CREATE OR REPLACE FUNCTION CDB_CreateOverviews(reloid REGCLASS, refscale_strategy regproc DEFAULT '_CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8,BOOLEAN)'::regprocedure)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CreateOverviews(reloid REGCLASS, refscale_strategy regproc DEFAULT '@extschema@._CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '@extschema@._CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8,BOOLEAN)'::regprocedure)
|
||||
RETURNS text[]
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -991,12 +988,12 @@ DECLARE
|
||||
BEGIN
|
||||
-- Use the default tolerance
|
||||
tolerance_px := 1.0;
|
||||
RETURN CDB_CreateOverviewsWithToleranceInPixels(reloid, tolerance_px, refscale_strategy, reduce_strategy);
|
||||
RETURN @extschema@.CDB_CreateOverviewsWithToleranceInPixels(reloid, tolerance_px, refscale_strategy, reduce_strategy);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Create overviews with additional parameter to define the desired detail/tolerance in pixels
|
||||
CREATE OR REPLACE FUNCTION CDB_CreateOverviewsWithToleranceInPixels(reloid REGCLASS, tolerance_px FLOAT8, refscale_strategy regproc DEFAULT '_CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8,BOOLEAN)'::regprocedure)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CreateOverviewsWithToleranceInPixels(reloid REGCLASS, tolerance_px FLOAT8, refscale_strategy regproc DEFAULT '@extschema@._CDB_Feature_Density_Ref_Z_Strategy(REGCLASS,FLOAT8)'::regprocedure, reduce_strategy regproc DEFAULT '@extschema@._CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8,BOOLEAN)'::regprocedure)
|
||||
RETURNS text[]
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -1048,7 +1045,7 @@ BEGIN
|
||||
|
||||
IF overview_tables IS NOT NULL AND array_length(overview_tables, 1) > 0 THEN
|
||||
SELECT EXISTS (
|
||||
SELECT * FROM CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
||||
SELECT * FROM @extschema@.CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
||||
) INTO has_counter_column;
|
||||
IF NOT has_counter_column THEN
|
||||
EXECUTE Format('
|
||||
@@ -1064,10 +1061,10 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
|
||||
-- Here are some older signatures of these functions, no longer in use.
|
||||
-- They must be droped here, after the (new) definition of the function `CDB_CreateOverviews`
|
||||
-- because that function used to contain references to them in the default argument values.
|
||||
DROP FUNCTION IF EXISTS _CDB_Feature_Density_Ref_Z_Strategy(REGCLASS);
|
||||
DROP FUNCTION IF EXISTS _CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
||||
DROP FUNCTION IF EXISTS _CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8);
|
||||
DROP FUNCTION IF EXISTS _CDB_GridClusterCentroid_Reduce_Strategy(REGCLASS, INTEGER, INTEGER, FLOAT8);
|
||||
DROP FUNCTION IF EXISTS _CDB_GridClusterSample_Reduce_Strategy(REGCLASS, INTEGER, INTEGER, FLOAT8);
|
||||
DROP FUNCTION IF EXISTS _CDB_Sampling_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
||||
DROP FUNCTION IF EXISTS _CDB_Sampling_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Feature_Density_Ref_Z_Strategy(REGCLASS);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_GridCluster_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_GridClusterCentroid_Reduce_Strategy(REGCLASS, INTEGER, INTEGER, FLOAT8);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_GridClusterSample_Reduce_Strategy(REGCLASS, INTEGER, INTEGER, FLOAT8);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Sampling_Reduce_Strategy(REGCLASS,INTEGER,INTEGER);
|
||||
DROP FUNCTION IF EXISTS @extschema@._CDB_Sampling_Reduce_Strategy(REGCLASS,INTEGER,INTEGER,FLOAT8);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- Auxiliary overviews FUNCTIONS
|
||||
|
||||
-- Maximum zoom level for which overviews may be created
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxOverviewLevel()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_MaxOverviewLevel()
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -18,7 +18,7 @@ AS $$
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- Maximum zoom level usable with integer coordinates
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxZoomLevel()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_MaxZoomLevel()
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -31,7 +31,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- that may contain user tables are returned.
|
||||
-- For each table, the regclass, schema name and table name are returned.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_UserTablesInSchema(schema_name text DEFAULT NULL)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_UserTablesInSchema(schema_name text DEFAULT NULL)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT
|
||||
@@ -43,7 +43,7 @@ AS $$
|
||||
WHERE c.relkind = 'r'
|
||||
AND c.relname NOT IN ('cdb_tablemetadata', 'cdb_analysis_catalog', 'cdb_conf', 'spatial_ref_sys')
|
||||
AND CASE WHEN schema_name IS NULL
|
||||
THEN n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', 'cartodb')
|
||||
THEN n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', '@extschema@')
|
||||
ELSE n.nspname = schema_name
|
||||
END;
|
||||
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
@@ -51,7 +51,7 @@ $$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
-- Pattern that can be used to detect overview tables and Extract
|
||||
-- the intended zoom level from the table name.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTableDiscriminator()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableDiscriminator()
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -63,18 +63,18 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- Pattern matched by the overview tables of a given base table name.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTablePattern(base_table TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTablePattern(base_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN _CDB_OverviewTableDiscriminator() || base_table;
|
||||
RETURN @extschema@._CDB_OverviewTableDiscriminator() || base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- tablename SIMILAR TO _CDB_OverviewTablePattern(base_table)
|
||||
|
||||
-- Name of an overview table, given the base table name and the Z level
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTableName(base_table TEXT, z INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableName(base_table TEXT, z INTEGER)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
@@ -84,39 +84,39 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- Condition to check if a tabla is an overview table of some base table
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_IsOverviewTableOf(base_table TEXT, otable TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_IsOverviewTableOf(base_table TEXT, otable TEXT)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN otable SIMILAR TO _CDB_OverviewTablePattern(base_table);
|
||||
RETURN otable SIMILAR TO @extschema@._CDB_OverviewTablePattern(base_table);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- Extract the Z level from an overview table name
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTableZ(otable TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableZ(otable TEXT)
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN substring(otable from _CDB_OverviewTableDiscriminator())::integer;
|
||||
RETURN substring(otable from @extschema@._CDB_OverviewTableDiscriminator())::integer;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- Name of the base table corresponding to an overview table
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewBaseTableName(overview_table TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewBaseTableName(overview_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
IF _CDB_OverviewTableZ(overview_table) IS NULL THEN
|
||||
IF @extschema@._CDB_OverviewTableZ(overview_table) IS NULL THEN
|
||||
RETURN overview_table;
|
||||
ELSE
|
||||
RETURN regexp_replace(overview_table, _CDB_OverviewTableDiscriminator(), '');
|
||||
RETURN regexp_replace(overview_table, @extschema@._CDB_OverviewTableDiscriminator(), '');
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewBaseTable(overview_table REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewBaseTable(overview_table REGCLASS)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -125,8 +125,8 @@ AS $$
|
||||
base_name TEXT;
|
||||
base_table REGCLASS;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(overview_table) INTO schema_name, table_name;
|
||||
base_name := _CDB_OverviewBaseTableName(table_name);
|
||||
SELECT * FROM @extschema@._cdb_split_table_name(overview_table) INTO schema_name, table_name;
|
||||
base_name := @extschema@._CDB_OverviewBaseTableName(table_name);
|
||||
IF base_name != table_name THEN
|
||||
base_table := Format('%I.%I', schema_name, base_name)::regclass;
|
||||
ELSE
|
||||
@@ -142,7 +142,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- reloid: oid of the table.
|
||||
-- Return (schema_name, table_name)
|
||||
-- note that returned names will be quoted if necessary
|
||||
CREATE OR REPLACE FUNCTION _cdb_split_table_name(reloid REGCLASS, OUT schema_name TEXT, OUT table_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_split_table_name(reloid REGCLASS, OUT schema_name TEXT, OUT table_name TEXT)
|
||||
AS $$
|
||||
BEGIN
|
||||
SELECT n.nspname, c.relname
|
||||
@@ -158,7 +158,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
|
||||
-- reloid: oid of the table.
|
||||
-- Return (schema_name, table_name)
|
||||
-- note that returned names will be quoted if necessary
|
||||
CREATE OR REPLACE FUNCTION _cdb_schema_name(reloid REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._cdb_schema_name(reloid REGCLASS)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
-- @param breaks The number of bins you want to find.
|
||||
--
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_QuantileBins(in_array numeric[], breaks int)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_QuantileBins(in_array numeric[], breaks int)
|
||||
RETURNS numeric[]
|
||||
AS $$
|
||||
SELECT
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
-- Regexp curtesy of Hubert Lubaczewski (depesz)
|
||||
-- Implemented in plpython for performance reasons
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_QueryStatements(query text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryStatements(query text)
|
||||
RETURNS SETOF TEXT AS $$
|
||||
import re
|
||||
pat = re.compile( r'''((?:[^'"$;]+|"[^"]*"|'[^']*'|(\$[^$]*\$).*?\2)+)''', re.DOTALL )
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
--
|
||||
-- Requires PostgreSQL 9.x+
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_QueryTablesText(query text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryTablesText(query text)
|
||||
RETURNS text[]
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -11,19 +11,16 @@ DECLARE
|
||||
rec RECORD;
|
||||
rec2 RECORD;
|
||||
BEGIN
|
||||
|
||||
|
||||
tables := '{}';
|
||||
|
||||
FOR rec IN SELECT CDB_QueryStatements(query) q LOOP
|
||||
|
||||
IF NOT ( rec.q ilike 'select%' or rec.q ilike 'with%' ) THEN
|
||||
--RAISE WARNING 'Skipping %', rec.q;
|
||||
CONTINUE;
|
||||
END IF;
|
||||
|
||||
FOR rec IN SELECT @extschema@.CDB_QueryStatements(query) q LOOP
|
||||
BEGIN
|
||||
EXECUTE 'EXPLAIN (FORMAT XML, VERBOSE) ' || rec.q INTO STRICT exp;
|
||||
EXCEPTION WHEN others THEN
|
||||
EXCEPTION WHEN syntax_error THEN
|
||||
-- We can get a syntax error if the user tries to EXPLAIN a DDL
|
||||
CONTINUE;
|
||||
WHEN others THEN
|
||||
-- TODO: if error is 'relation "xxxxxx" does not exist', take xxxxxx as
|
||||
-- the affected table ?
|
||||
RAISE WARNING 'CDB_QueryTables cannot explain query: % (%: %)', rec.q, SQLSTATE, SQLERRM;
|
||||
@@ -69,10 +66,10 @@ $$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
|
||||
|
||||
-- Keep CDB_QueryTables with same signature for backwards compatibility.
|
||||
-- It should probably be removed in the future.
|
||||
CREATE OR REPLACE FUNCTION CDB_QueryTables(query text)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryTables(query text)
|
||||
RETURNS name[]
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN CDB_QueryTablesText(query)::name[];
|
||||
RETURN @extschema@.CDB_QueryTablesText(query)::name[];
|
||||
END
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CREATE OR REPLACE FUNCTION cartodb._CDB_total_relation_size(_schema_name TEXT, _table_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_total_relation_size(_schema_name TEXT, _table_name TEXT)
|
||||
RETURNS bigint AS
|
||||
$$
|
||||
DECLARE relation_size bigint := 0;
|
||||
@@ -7,7 +7,7 @@ BEGIN
|
||||
SELECT pg_total_relation_size(format('"%s"."%s"', _schema_name, _table_name)) INTO relation_size;
|
||||
EXCEPTION
|
||||
WHEN undefined_table OR OTHERS THEN
|
||||
RAISE NOTICE 'cartodb._CDB_total_relation_size(''%'', ''%'') caught error: % (%)', _schema_name, _table_name, SQLERRM, SQLSTATE;
|
||||
RAISE NOTICE '@extschema@._CDB_total_relation_size(''%'', ''%'') caught error: % (%)', _schema_name, _table_name, SQLERRM, SQLSTATE;
|
||||
END;
|
||||
RETURN relation_size;
|
||||
END;
|
||||
@@ -15,7 +15,7 @@ $$
|
||||
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Return the estimated size of user data. Used for quota checking.
|
||||
CREATE OR REPLACE FUNCTION CDB_UserDataSize(schema_name TEXT)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize(schema_name TEXT)
|
||||
RETURNS bigint AS
|
||||
$$
|
||||
DECLARE
|
||||
@@ -26,20 +26,20 @@ BEGIN
|
||||
WHERE o_table_schema = schema_name AND o_table_catalog = current_database()
|
||||
),
|
||||
user_tables AS (
|
||||
SELECT table_name FROM _CDB_NonAnalysisTablesInSchema(schema_name)
|
||||
SELECT table_name FROM @extschema@._CDB_NonAnalysisTablesInSchema(schema_name)
|
||||
),
|
||||
table_cat AS (
|
||||
SELECT
|
||||
table_name,
|
||||
(
|
||||
EXISTS(select * from raster_tables where o_table_name = table_name)
|
||||
OR table_name SIMILAR TO _CDB_OverviewTableDiscriminator() || '[\w\d]*'
|
||||
OR table_name SIMILAR TO @extschema@._CDB_OverviewTableDiscriminator() || '[\w\d]*'
|
||||
) AS is_overview,
|
||||
EXISTS(SELECT * FROM raster_tables WHERE r_table_name = table_name) AS is_raster
|
||||
FROM user_tables
|
||||
),
|
||||
sizes AS (
|
||||
SELECT COALESCE(INT8(SUM(cartodb._CDB_total_relation_size(schema_name, table_name)))) table_size,
|
||||
SELECT COALESCE(INT8(SUM(@extschema@._CDB_total_relation_size(schema_name, table_name)))) table_size,
|
||||
CASE
|
||||
WHEN is_overview THEN 0
|
||||
WHEN is_raster THEN 1
|
||||
@@ -60,15 +60,15 @@ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Return the estimated size of user data. Used for quota checking.
|
||||
-- Implicit schema version for backwards compatibility
|
||||
CREATE OR REPLACE FUNCTION CDB_UserDataSize()
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize()
|
||||
RETURNS bigint AS
|
||||
$$
|
||||
SELECT CDB_UserDataSize('public');
|
||||
SELECT @extschema@.CDB_UserDataSize('public');
|
||||
$$
|
||||
LANGUAGE 'sql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
-- Triggers cannot have declared arguments: pbfact float8, qmax int8, schema_name text
|
||||
CREATE OR REPLACE FUNCTION CDB_CheckQuota()
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_CheckQuota()
|
||||
RETURNS trigger AS
|
||||
$$
|
||||
DECLARE
|
||||
@@ -80,7 +80,7 @@ DECLARE
|
||||
BEGIN
|
||||
IF TG_NARGS = 3 THEN
|
||||
schema_name := TG_ARGV[2];
|
||||
IF cartodb.schema_exists(schema_name) = false THEN
|
||||
IF @extschema@.schema_exists(schema_name) = false THEN
|
||||
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
|
||||
END IF;
|
||||
ELSE
|
||||
@@ -111,7 +111,7 @@ BEGIN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
SELECT public.CDB_UserDataSize(schema_name) INTO quota;
|
||||
SELECT @extschema@.CDB_UserDataSize(schema_name) INTO quota;
|
||||
IF quota > qmax THEN
|
||||
RAISE EXCEPTION 'Quota exceeded by %KB', (quota-qmax)/1024;
|
||||
ELSE RAISE DEBUG 'User quota in bytes: % < % (max allowed)', quota, qmax;
|
||||
@@ -124,13 +124,13 @@ $$
|
||||
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
|
||||
RETURNS int8 AS
|
||||
$$
|
||||
DECLARE
|
||||
sql text;
|
||||
BEGIN
|
||||
IF cartodb.schema_exists(schema_name::text) = false THEN
|
||||
IF @extschema@.schema_exists(schema_name::text) = false THEN
|
||||
RAISE EXCEPTION 'Invalid schema name "%"', schema_name::text;
|
||||
END IF;
|
||||
|
||||
@@ -145,11 +145,11 @@ $$
|
||||
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(bytes int8)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(bytes int8)
|
||||
RETURNS int8 AS
|
||||
$$
|
||||
BEGIN
|
||||
return public.CDB_SetUserQuotaInBytes('public', bytes);
|
||||
return @extschema@.CDB_SetUserQuotaInBytes('public', bytes);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
--
|
||||
--
|
||||
-- }{
|
||||
CREATE OR REPLACE FUNCTION CDB_RandomTids(in_table regclass, in_nsamples integer)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_RandomTids(in_table regclass, in_nsamples integer)
|
||||
RETURNS tid[]
|
||||
AS $$
|
||||
DECLARE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- In older versions of the extension, CDB_RectangleGrid had a different signature
|
||||
DROP FUNCTION IF EXISTS cartodb.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8, GEOMETRY);
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8, GEOMETRY);
|
||||
|
||||
--
|
||||
-- Fill given extent with a rectangular coverage
|
||||
@@ -22,7 +22,7 @@ DROP FUNCTION IF EXISTS cartodb.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8, GEOM
|
||||
-- if the grid requires more cells to cover the extent
|
||||
-- and exception will occur.
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_RectangleGrid(ext GEOMETRY, width FLOAT8, height FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_RectangleGrid(ext GEOMETRY, width FLOAT8, height FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
|
||||
RETURNS SETOF GEOMETRY
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -44,17 +44,17 @@ DECLARE
|
||||
srid INTEGER;
|
||||
BEGIN
|
||||
|
||||
srid := ST_SRID(ext);
|
||||
srid := @postgisschema@.ST_SRID(ext);
|
||||
|
||||
xoff := 0;
|
||||
yoff := 0;
|
||||
|
||||
IF origin IS NOT NULL THEN
|
||||
IF ST_SRID(origin) != srid THEN
|
||||
IF @postgisschema@.ST_SRID(origin) != srid THEN
|
||||
RAISE EXCEPTION 'SRID mismatch between extent (%) and origin (%)', srid, ST_SRID(origin);
|
||||
END IF;
|
||||
xoff := ST_X(origin);
|
||||
yoff := ST_Y(origin);
|
||||
xoff := @postgisschema@.ST_X(origin);
|
||||
yoff := @postgisschema@.ST_Y(origin);
|
||||
END IF;
|
||||
|
||||
--RAISE DEBUG 'X offset: %', xoff;
|
||||
@@ -72,11 +72,11 @@ BEGIN
|
||||
vstep := height;
|
||||
|
||||
-- Tweak horizontal start on hstep grid from origin
|
||||
hstart := xoff + ceil((ST_XMin(ext)-xoff)/hstep)*hstep;
|
||||
hstart := xoff + ceil((@postgisschema@.ST_XMin(ext)-xoff)/hstep)*hstep;
|
||||
--RAISE DEBUG 'hstart: %', hstart;
|
||||
|
||||
-- Tweak vertical start on vstep grid from origin
|
||||
vstart := yoff + ceil((ST_Ymin(ext)-yoff)/vstep)*vstep;
|
||||
vstart := yoff + ceil((@postgisschema@.ST_Ymin(ext)-yoff)/vstep)*vstep;
|
||||
--RAISE DEBUG 'vstart: %', vstart;
|
||||
|
||||
hend := ST_XMax(ext);
|
||||
@@ -94,10 +94,10 @@ BEGIN
|
||||
x := hstart;
|
||||
WHILE x < hend LOOP -- over X
|
||||
y := vstart;
|
||||
h := ST_MakeEnvelope(x-hw, y-hh, x+hw, y+hh, srid);
|
||||
h := @postgisschema@.ST_MakeEnvelope(x-hw, y-hh, x+hw, y+hh, srid);
|
||||
WHILE y < vend LOOP -- over Y
|
||||
RETURN NEXT h;
|
||||
h := ST_Translate(h, 0, vstep);
|
||||
h := @postgisschema@.ST_Translate(h, 0, vstep);
|
||||
y := yoff + round(((y + vstep)-yoff)/ygrd)*ygrd; -- round to grid
|
||||
END LOOP;
|
||||
x := xoff + round(((x + hstep)-xoff)/xgrd)*xgrd; -- round to grid
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
---- Make sure 'cartodb' is in database search path
|
||||
---- Make sure '@extschema@' is in database search path
|
||||
DO
|
||||
$$
|
||||
DECLARE
|
||||
@@ -8,13 +8,13 @@ BEGIN
|
||||
SELECT reset_val INTO var_cur_search_path
|
||||
FROM pg_settings WHERE name = 'search_path';
|
||||
|
||||
IF var_cur_search_path LIKE '%cartodb%' THEN
|
||||
RAISE DEBUG '"cartodb" already in database search_path';
|
||||
IF var_cur_search_path LIKE '%@extschema@%' THEN
|
||||
RAISE DEBUG '"@extschema@" already in database search_path';
|
||||
ELSE
|
||||
var_cur_search_path := var_cur_search_path || ', "cartodb"';
|
||||
var_cur_search_path := var_cur_search_path || ', "@extschema@"';
|
||||
EXECUTE 'ALTER DATABASE ' || quote_ident(current_database()) ||
|
||||
' SET search_path = ' || var_cur_search_path;
|
||||
RAISE DEBUG '"cartodb" has been added to end of database search_path';
|
||||
RAISE DEBUG '"@extschema@" has been added to end of database search_path';
|
||||
END IF;
|
||||
|
||||
-- Reset search_path
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
--
|
||||
|
||||
-- Calculate kurtosis
|
||||
CREATE OR REPLACE FUNCTION CDB_Kurtosis ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Kurtosis ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
|
||||
DECLARE
|
||||
a numeric;
|
||||
c numeric;
|
||||
@@ -32,7 +32,7 @@ END;
|
||||
$$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
-- Calculate skewness
|
||||
CREATE OR REPLACE FUNCTION CDB_Skewness ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Skewness ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
|
||||
DECLARE
|
||||
a numeric;
|
||||
c numeric;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- Convert string to date
|
||||
--
|
||||
DROP FUNCTION IF EXISTS CDB_StringToDate(character varying);
|
||||
CREATE OR REPLACE FUNCTION CDB_StringToDate(input character varying)
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_StringToDate(character varying);
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_StringToDate(input character varying)
|
||||
RETURNS TIMESTAMP AS $$
|
||||
DECLARE output TIMESTAMP;
|
||||
BEGIN
|
||||
|
||||
167
scripts-available/CDB_SyncTable.sql
Normal file
167
scripts-available/CDB_SyncTable.sql
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Gets the column names of a given table.
|
||||
|
||||
Sample usage:
|
||||
|
||||
SELECT @extschema@._CDB_GetColumns('public.films');
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_GetColumns(src_table REGCLASS)
|
||||
RETURNS SETOF NAME
|
||||
AS $$
|
||||
SELECT
|
||||
a.attname as "colname"
|
||||
FROM
|
||||
pg_catalog.pg_attribute a
|
||||
WHERE
|
||||
a.attnum > 0
|
||||
AND NOT a.attisdropped
|
||||
AND a.attrelid = (
|
||||
SELECT c.oid
|
||||
FROM pg_catalog.pg_class c
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.oid = src_table::oid
|
||||
AND pg_catalog.pg_table_is_visible(c.oid)
|
||||
)
|
||||
ORDER BY a.attnum;
|
||||
$$ LANGUAGE sql STABLE PARALLEL UNSAFE;
|
||||
|
||||
|
||||
/*
|
||||
Given an array of quoted column names, it generates an UPDATE SET
|
||||
clause with the following form:
|
||||
|
||||
the_geom = changed.the_geom,
|
||||
id = changed.id,
|
||||
elevation = changed.elevation
|
||||
|
||||
Example of usage:
|
||||
|
||||
SELECT @extschema@.__CDB_GetUpdateSetClause('{the_geom, id, elevation}', 'changed');
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION @extschema@.__CDB_GetUpdateSetClause(colnames TEXT[], update_source TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
set_clause_list TEXT[];
|
||||
col TEXT;
|
||||
BEGIN
|
||||
FOREACH col IN ARRAY colnames
|
||||
LOOP
|
||||
set_clause_list := array_append(set_clause_list, format('%1$s = %2$s.%1$s', col, update_source));
|
||||
END lOOP;
|
||||
RETURN array_to_string(set_clause_list, ', ');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
|
||||
/*
|
||||
Given a prefix, generate a safe unique NAME for a temp table.
|
||||
|
||||
Example of usage:
|
||||
|
||||
SELECT @extschema@.__CDB_GenerateUniqueName('src_sync'); --> src_sync_718794_120106
|
||||
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION @extschema@.__CDB_GenerateUniqueName(prefix TEXT)
|
||||
RETURNS NAME
|
||||
AS $$
|
||||
SELECT format('%s_%s_%s', prefix, txid_current(), (random()*1000000)::int)::NAME;
|
||||
$$ LANGUAGE sql VOLATILE PARALLEL UNSAFE;
|
||||
|
||||
/*
|
||||
Given a table name and an array of column names,
|
||||
return array of column names qualified with the table name and quoted when necessary
|
||||
tablename and colnames should be properly quoted, and for this reason the type NAME is not
|
||||
used for them (with quotes they could exceed the maximum identifier length)
|
||||
|
||||
Example of usage:
|
||||
|
||||
SELECT @extschema@.__CDB_QualifyColumns('t', ARRAY['a','"b-1"']); --> ARRAY['t.a','t."b-1"']
|
||||
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION @extschema@.__CDB_QualifyColumns(tablename NAME, colnames NAME[]) RETURNS TEXT[] AS
|
||||
$$
|
||||
SELECT array_agg(tablename || '.' || _colname) from unnest(colnames) _colname;
|
||||
$$ LANGUAGE sql IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
/*
|
||||
A Table Syncer
|
||||
|
||||
Assumptions:
|
||||
- Both tables contain a consistent cartodb_id column
|
||||
- Destination table has all columns of the source or does not exist
|
||||
|
||||
Sample usage:
|
||||
|
||||
SELECT CDB_SyncTable('radar_stations', 'public', 'syncdest');
|
||||
SELECT CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest', '{the_geom, the_geom_webmercator}');
|
||||
|
||||
*/
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SyncTable(src_table REGCLASS, dst_schema REGNAMESPACE, dst_table NAME, skip_cols NAME[] = '{}')
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
fq_dest_table TEXT;
|
||||
|
||||
colnames TEXT[];
|
||||
dst_colnames TEXT;
|
||||
src_colnames TEXT;
|
||||
|
||||
update_set_clause TEXT;
|
||||
|
||||
num_rows BIGINT;
|
||||
err_context text;
|
||||
|
||||
t timestamptz;
|
||||
BEGIN
|
||||
-- If the destination table does not exist, just copy the source table
|
||||
fq_dest_table := format('%s.%I', dst_schema, dst_table);
|
||||
EXECUTE format('CREATE TABLE IF NOT EXISTS %s as TABLE %s', fq_dest_table, src_table);
|
||||
GET DIAGNOSTICS num_rows = ROW_COUNT;
|
||||
IF num_rows > 0 THEN
|
||||
RAISE NOTICE 'INSERTED % row(s)', num_rows;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
skip_cols := skip_cols || '{cartodb_id}';
|
||||
|
||||
-- Get the list of columns from the source table, excluding skip_cols
|
||||
SELECT ARRAY(SELECT quote_ident(c) FROM @extschema@._CDB_GetColumns(src_table) as c EXCEPT SELECT unnest(skip_cols)) INTO colnames;
|
||||
|
||||
-- Deal with deleted rows: ids in dest but not in source
|
||||
t := clock_timestamp();
|
||||
EXECUTE format(
|
||||
'DELETE FROM %1$s _dst WHERE NOT EXISTS (SELECT * FROM %2$s _src WHERE _src.cartodb_id=_dst.cartodb_id)',
|
||||
fq_dest_table, src_table);
|
||||
GET DIAGNOSTICS num_rows = ROW_COUNT;
|
||||
RAISE NOTICE 'DELETED % row(s)', num_rows;
|
||||
RAISE DEBUG 'DELETE time (s): %', clock_timestamp() - t;
|
||||
|
||||
-- Deal with inserted rows: ids in source but not in dest
|
||||
t := clock_timestamp();
|
||||
EXECUTE format('
|
||||
INSERT INTO %1$s(cartodb_id, %2$s)
|
||||
SELECT cartodb_id, %2$s FROM %3$s _src WHERE NOT EXISTS (SELECT * FROM %1$s _dst WHERE _src.cartodb_id=_dst.cartodb_id)
|
||||
', fq_dest_table, array_to_string(colnames, ','), src_table);
|
||||
GET DIAGNOSTICS num_rows = ROW_COUNT;
|
||||
RAISE NOTICE 'INSERTED % row(s)', num_rows;
|
||||
RAISE DEBUG 'INSERT time (s): %', clock_timestamp() - t;
|
||||
|
||||
-- Deal with modified rows: ids in source and dest but different hashes
|
||||
t := clock_timestamp();
|
||||
update_set_clause := @extschema@.__CDB_GetUpdateSetClause(colnames, '_changed');
|
||||
dst_colnames := array_to_string(@extschema@.__CDB_QualifyColumns('_dst', colnames), ',');
|
||||
src_colnames := array_to_string(@extschema@.__CDB_QualifyColumns('_src', colnames), ',');
|
||||
EXECUTE format('
|
||||
UPDATE %1$s _update SET %2$s
|
||||
FROM (
|
||||
SELECT _src.* FROM %3$s _src JOIN %1$s _dst ON (_dst.cartodb_id = _src.cartodb_id)
|
||||
WHERE md5(ROW(%4$s)::text) <> md5(ROW(%5$s)::text)
|
||||
) _changed
|
||||
WHERE _update.cartodb_id = _changed.cartodb_id;
|
||||
', fq_dest_table, update_set_clause, src_table, dst_colnames, src_colnames);
|
||||
GET DIAGNOSTICS num_rows = ROW_COUNT;
|
||||
RAISE NOTICE 'MODIFIED % row(s)', num_rows;
|
||||
RAISE DEBUG 'UPDATE time (s): %', clock_timestamp() - t;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Function returning indexes for a table
|
||||
CREATE OR REPLACE FUNCTION CDB_TableIndexes(REGCLASS)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableIndexes(REGCLASS)
|
||||
RETURNS TABLE(index_name name, index_unique bool, index_primary bool, index_keys text array)
|
||||
AS $$
|
||||
|
||||
@@ -24,4 +24,4 @@ $$ LANGUAGE SQL STABLE PARALLEL SAFE;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
|
||||
GRANT EXECUTE ON FUNCTION CDB_TableIndexes(REGCLASS) TO public;
|
||||
GRANT EXECUTE ON FUNCTION @extschema@.CDB_TableIndexes(REGCLASS) TO public;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
public.CDB_TableMetadata (
|
||||
@extschema@.CDB_TableMetadata (
|
||||
tabname regclass not null primary key,
|
||||
updated_at timestamp with time zone not null default now()
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW public.CDB_TableMetadata_Text AS
|
||||
CREATE OR REPLACE VIEW @extschema@.CDB_TableMetadata_Text AS
|
||||
SELECT FORMAT('%I.%I', n.nspname::text, c.relname::text) tabname, updated_at
|
||||
FROM public.CDB_TableMetadata, pg_catalog.pg_class c
|
||||
FROM @extschema@.CDB_TableMetadata m JOIN pg_catalog.pg_class c ON m.tabname::oid = c.oid
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid;
|
||||
|
||||
-- No one can see this
|
||||
-- Updates are only possible trough the security definer trigger
|
||||
-- GRANT SELECT ON public.CDB_TableMetadata TO public;
|
||||
-- GRANT SELECT ON @extschema@.CDB_TableMetadata TO public;
|
||||
|
||||
--
|
||||
-- Trigger logging updated_at in the CDB_TableMetadata
|
||||
@@ -27,17 +27,17 @@ CREATE OR REPLACE VIEW public.CDB_TableMetadata_Text AS
|
||||
--
|
||||
-- NOTE: _never_ attach to CDB_TableMetadata ...
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_TableMetadata_Trigger()
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableMetadata_Trigger()
|
||||
RETURNS trigger AS
|
||||
$$
|
||||
BEGIN
|
||||
-- Guard against infinite loop
|
||||
IF TG_RELID = 'public.CDB_TableMetadata'::regclass::oid THEN
|
||||
IF TG_RELID = '@extschema@.CDB_TableMetadata'::regclass::oid THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
-- Cleanup stale entries
|
||||
DELETE FROM public.CDB_TableMetadata
|
||||
DELETE FROM @extschema@.CDB_TableMetadata
|
||||
WHERE NOT EXISTS (
|
||||
SELECT oid FROM pg_class WHERE oid = tabname
|
||||
);
|
||||
@@ -45,11 +45,11 @@ BEGIN
|
||||
WITH nv as (
|
||||
SELECT TG_RELID as tabname, NOW() as t
|
||||
), updated as (
|
||||
UPDATE public.CDB_TableMetadata x SET updated_at = nv.t
|
||||
UPDATE @extschema@.CDB_TableMetadata x SET updated_at = nv.t
|
||||
FROM nv WHERE x.tabname = nv.tabname
|
||||
RETURNING x.tabname
|
||||
)
|
||||
INSERT INTO public.CDB_TableMetadata SELECT nv.*
|
||||
INSERT INTO @extschema@.CDB_TableMetadata SELECT nv.*
|
||||
FROM nv LEFT JOIN updated USING(tabname)
|
||||
WHERE updated.tabname IS NULL;
|
||||
|
||||
@@ -62,7 +62,7 @@ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
-- Trigger invalidating varnish whenever CDB_TableMetadata
|
||||
-- record change.
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION _CDB_TableMetadata_Updated()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_TableMetadata_Updated()
|
||||
RETURNS trigger AS
|
||||
$$
|
||||
DECLARE
|
||||
@@ -93,14 +93,14 @@ BEGIN
|
||||
--
|
||||
|
||||
-- Call the first varnish invalidation function owned
|
||||
-- by a superuser found in cartodb or public schema
|
||||
-- by a superuser found in @extschema@ or public schema
|
||||
-- (in that order)
|
||||
found := false;
|
||||
FOR rec IN SELECT u.usesuper, u.usename, n.nspname, p.proname
|
||||
FROM pg_proc p, pg_namespace n, pg_user u
|
||||
WHERE p.proname = 'cdb_invalidate_varnish'
|
||||
AND p.pronamespace = n.oid
|
||||
AND n.nspname IN ('public', 'cartodb')
|
||||
AND n.nspname IN ('public', '@extschema@')
|
||||
AND u.usesysid = p.proowner
|
||||
AND u.usesuper
|
||||
ORDER BY n.nspname
|
||||
@@ -119,26 +119,26 @@ END;
|
||||
$$
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE SECURITY DEFINER;
|
||||
|
||||
DROP TRIGGER IF EXISTS table_modified ON public.CDB_TableMetadata;
|
||||
DROP TRIGGER IF EXISTS table_modified ON @extschema@.CDB_TableMetadata;
|
||||
-- NOTE: on DELETE we would be unable to convert the table
|
||||
-- oid (regclass) to its name
|
||||
CREATE TRIGGER table_modified AFTER INSERT OR UPDATE
|
||||
ON public.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
_CDB_TableMetadata_Updated();
|
||||
ON @extschema@.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
@extschema@._CDB_TableMetadata_Updated();
|
||||
|
||||
|
||||
-- similar to TOUCH(1) in unix filesystems but for table in cdb_tablemetadata
|
||||
CREATE OR REPLACE FUNCTION public.CDB_TableMetadataTouch(tablename regclass)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableMetadataTouch(tablename regclass)
|
||||
RETURNS void AS
|
||||
$$
|
||||
BEGIN
|
||||
WITH upsert AS (
|
||||
UPDATE public.cdb_tablemetadata
|
||||
UPDATE @extschema@.cdb_tablemetadata
|
||||
SET updated_at = NOW()
|
||||
WHERE tabname = tablename
|
||||
RETURNING *
|
||||
)
|
||||
INSERT INTO public.cdb_tablemetadata (tabname, updated_at)
|
||||
INSERT INTO @extschema@.cdb_tablemetadata (tabname, updated_at)
|
||||
SELECT tablename, NOW()
|
||||
WHERE NOT EXISTS (SELECT * FROM upsert);
|
||||
END;
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
-- for web mercator by "clipping" anything outside to the valid
|
||||
-- range.
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION CDB_TransformToWebmercator(geom geometry)
|
||||
RETURNS geometry
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_TransformToWebmercator(geom @postgisschema@.geometry)
|
||||
RETURNS @postgisschema@.geometry
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
valid_extent GEOMETRY;
|
||||
latlon_input GEOMETRY;
|
||||
clipped_input GEOMETRY;
|
||||
to_webmercator GEOMETRY;
|
||||
ret GEOMETRY;
|
||||
valid_extent @postgisschema@.GEOMETRY;
|
||||
latlon_input @postgisschema@.GEOMETRY;
|
||||
clipped_input @postgisschema@.GEOMETRY;
|
||||
to_webmercator @postgisschema@.GEOMETRY;
|
||||
ret @postgisschema@.GEOMETRY;
|
||||
BEGIN
|
||||
|
||||
IF ST_Srid(geom) = 3857 THEN
|
||||
IF @postgisschema@.ST_Srid(geom) = 3857 THEN
|
||||
RETURN geom;
|
||||
END IF;
|
||||
|
||||
@@ -27,49 +27,53 @@ BEGIN
|
||||
-- to -85.0511 to 85.0511 but as long as proj
|
||||
-- does not complain we are happy
|
||||
--
|
||||
valid_extent := ST_MakeEnvelope(-180, -89, 180, 89, 4326);
|
||||
valid_extent := @postgisschema@.ST_MakeEnvelope(-180, -89, 180, 89, 4326);
|
||||
|
||||
-- Then we transform to WGS84 latlon, which is
|
||||
-- where we have known coordinates for the clipping
|
||||
--
|
||||
latlon_input := ST_Transform(geom, 4326);
|
||||
--
|
||||
latlon_input := @postgisschema@.ST_Transform(geom, 4326);
|
||||
|
||||
-- Don't bother clipping if the geometry boundary doesn't
|
||||
-- go outside the valid extent.
|
||||
IF latlon_input @ valid_extent THEN
|
||||
RETURN ST_Transform(latlon_input, 3857);
|
||||
BEGIN
|
||||
RETURN @postgisschema@.ST_Transform(latlon_input, 3857);
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RETURN NULL;
|
||||
END;
|
||||
END IF;
|
||||
|
||||
-- Since we're going to use ST_Intersection on input
|
||||
-- we'd better ensure the input is valid
|
||||
-- TODO: only do this if the first ST_Intersection fails ?
|
||||
IF ST_Dimension(geom) != 0 AND
|
||||
IF @postgisschema@.ST_Dimension(geom) != 0 AND
|
||||
-- See http://trac.osgeo.org/postgis/ticket/1719
|
||||
GeometryType(geom) != 'GEOMETRYCOLLECTION'
|
||||
@postgisschema@.GeometryType(geom) != 'GEOMETRYCOLLECTION'
|
||||
THEN
|
||||
BEGIN
|
||||
latlon_input := ST_MakeValid(latlon_input);
|
||||
latlon_input := @postgisschema@.ST_MakeValid(latlon_input);
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
-- See http://github.com/Vizzuality/cartodb/issues/931
|
||||
RAISE WARNING 'Could not clean input geometry: %', SQLERRM;
|
||||
RETURN NULL;
|
||||
END;
|
||||
latlon_input := ST_CollectionExtract(latlon_input, ST_Dimension(geom)+1);
|
||||
latlon_input := @postgisschema@.ST_CollectionExtract(latlon_input, ST_Dimension(geom)+1);
|
||||
END IF;
|
||||
|
||||
-- Then we clip, trying to retain the input type
|
||||
-- TODO: catch exceptions here too ?
|
||||
clipped_input := ST_Intersection(latlon_input, valid_extent);
|
||||
clipped_input := @postgisschema@.ST_Intersection(latlon_input, valid_extent);
|
||||
|
||||
-- We transform to web mercator
|
||||
to_webmercator := ST_Transform(clipped_input, 3857);
|
||||
to_webmercator := @postgisschema@.ST_Transform(clipped_input, 3857);
|
||||
|
||||
-- Finally we convert EMPTY to NULL
|
||||
-- Finally we convert EMPTY to NULL
|
||||
-- See https://github.com/Vizzuality/cartodb/issues/706
|
||||
-- And retain "multi" status
|
||||
ret := CASE WHEN ST_IsEmpty(to_webmercator) THEN NULL::geometry
|
||||
WHEN GeometryType(geom) LIKE 'MULTI%' THEN ST_Multi(to_webmercator)
|
||||
ret := CASE WHEN @postgisschema@.ST_IsEmpty(to_webmercator) THEN NULL::@postgisschema@.geometry
|
||||
WHEN @postgisschema@.GeometryType(geom) LIKE 'MULTI%' THEN @postgisschema@.ST_Multi(to_webmercator)
|
||||
ELSE to_webmercator
|
||||
END;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
--
|
||||
-- Currently accepted permissions are: 'public', 'private' or 'all'
|
||||
--
|
||||
DROP FUNCTION IF EXISTS CDB_UserTables(text);
|
||||
CREATE OR REPLACE FUNCTION CDB_UserTables(perm text DEFAULT 'all')
|
||||
DROP FUNCTION IF EXISTS @extschema@.CDB_UserTables(text);
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserTables(perm text DEFAULT 'all')
|
||||
RETURNS SETOF name
|
||||
AS $$
|
||||
|
||||
@@ -15,7 +15,7 @@ FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relkind = 'r'
|
||||
AND c.relname NOT IN ('cdb_tablemetadata', 'cdb_analysis_catalog', 'cdb_conf', 'spatial_ref_sys')
|
||||
AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', 'cartodb')
|
||||
AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', '@extschema@')
|
||||
AND CASE WHEN perm = 'public' THEN has_table_privilege('publicuser', c.oid, 'SELECT')
|
||||
WHEN perm = 'private' THEN has_table_privilege(current_user, c.oid, 'SELECT') AND NOT has_table_privilege('publicuser', c.oid, 'SELECT')
|
||||
WHEN perm = 'all' THEN has_table_privilege(current_user, c.oid, 'SELECT') OR has_table_privilege('publicuser', c.oid, 'SELECT')
|
||||
@@ -25,4 +25,4 @@ $$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
|
||||
GRANT EXECUTE ON FUNCTION CDB_UserTables(text) TO public;
|
||||
GRANT EXECUTE ON FUNCTION @extschema@.CDB_UserTables(text) TO public;
|
||||
|
||||
6
scripts-available/CDB_Username.sql
Normal file
6
scripts-available/CDB_Username.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
-- Returns the cartodb username of the current PostgreSQL session
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_Username()
|
||||
RETURNS text
|
||||
AS $$
|
||||
SELECT @extschema@.CDB_Conf_GetConf(CONCAT('api_keys_', session_user))->>'username';
|
||||
$$ LANGUAGE SQL STABLE PARALLEL SAFE SECURITY DEFINER;
|
||||
@@ -1,7 +1,7 @@
|
||||
-- {
|
||||
-- Return pixel resolution at the given zoom level
|
||||
-- }{
|
||||
CREATE OR REPLACE FUNCTION CDB_XYZ_Resolution(z INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_XYZ_Resolution(z INTEGER)
|
||||
RETURNS FLOAT8
|
||||
AS $$
|
||||
-- circumference divided by 256 is z0 resolution, then divide by 2^z
|
||||
@@ -15,7 +15,7 @@ $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE STRICT;
|
||||
-- SRID of the returned polygon is forceably 3857
|
||||
--
|
||||
-- }{
|
||||
CREATE OR REPLACE FUNCTION CDB_XYZ_Extent(x INTEGER, y INTEGER, z INTEGER)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_XYZ_Extent(x INTEGER, y INTEGER, z INTEGER)
|
||||
RETURNS GEOMETRY
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -34,7 +34,7 @@ BEGIN
|
||||
-- Size of each tile in pixels (1:1 aspect ratio)
|
||||
tile_size := 256;
|
||||
|
||||
initial_resolution := CDB_XYZ_Resolution(0);
|
||||
initial_resolution := @extschema@.CDB_XYZ_Resolution(0);
|
||||
--RAISE DEBUG 'Initial resolution: %', initial_resolution;
|
||||
|
||||
origin_shift := (initial_resolution * tile_size) / 2.0;
|
||||
@@ -56,7 +56,7 @@ BEGIN
|
||||
--RAISE DEBUG 'ymin: %', ymin;
|
||||
--RAISE DEBUG 'ymax: %', ymax;
|
||||
|
||||
RETURN ST_MakeEnvelope(xmin, ymin, xmax, ymax, 3857);
|
||||
RETURN @postgisschema@.ST_MakeEnvelope(xmin, ymin, xmax, ymax, 3857);
|
||||
END
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL SAFE;
|
||||
-- }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Maximum supported zoom level
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxSupportedZoom()
|
||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_MaxSupportedZoom()
|
||||
RETURNS int
|
||||
LANGUAGE SQL
|
||||
IMMUTABLE PARALLEL SAFE
|
||||
@@ -12,7 +12,7 @@ AS $$
|
||||
SELECT 29;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_ZoomFromScale(scaleDenominator numeric)
|
||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_ZoomFromScale(scaleDenominator numeric)
|
||||
RETURNS int
|
||||
LANGUAGE SQL
|
||||
IMMUTABLE PARALLEL SAFE
|
||||
@@ -24,12 +24,12 @@ AS $$
|
||||
NULL
|
||||
WHEN scaleDenominator = 0 THEN
|
||||
-- Actual zoom level would be infinite
|
||||
_CDB_MaxSupportedZoom()
|
||||
@extschema@._CDB_MaxSupportedZoom()
|
||||
ELSE
|
||||
CAST (
|
||||
LEAST(
|
||||
ROUND(LOG(2, 559082264.028/scaleDenominator)),
|
||||
_CDB_MaxSupportedZoom()
|
||||
@extschema@._CDB_MaxSupportedZoom()
|
||||
)
|
||||
AS INTEGER)
|
||||
END;
|
||||
|
||||
1
scripts-enabled/290-CDB_GhostTables.sql
Symbolic link
1
scripts-enabled/290-CDB_GhostTables.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_GhostTables.sql
|
||||
1
scripts-enabled/300-CDB_OAuth.sql
Symbolic link
1
scripts-enabled/300-CDB_OAuth.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_OAuth.sql
|
||||
1
scripts-enabled/900-CDB_DDLTriggers.sql
Symbolic link
1
scripts-enabled/900-CDB_DDLTriggers.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_DDLTriggers.sql
|
||||
1
scripts-enabled/920-CDB_Username.sql
Symbolic link
1
scripts-enabled/920-CDB_Username.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_Username.sql
|
||||
1
scripts-enabled/CDB_SyncTable.sql
Symbolic link
1
scripts-enabled/CDB_SyncTable.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_SyncTable.sql
|
||||
@@ -7,7 +7,7 @@ SELECT _CDB_AnalysisDataSize('public');
|
||||
CREATE TABLE analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d5(id int);
|
||||
CREATE TABLE analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94(id int);
|
||||
CREATE TABLE analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da9(id int);
|
||||
SELECT _CDB_AnalysisTablesInSchema('public');
|
||||
SELECT _CDB_AnalysisTablesInSchema('public') t ORDER BY t;
|
||||
SELECT _CDB_AnalysisDataSize('public');
|
||||
SELECT CDB_CheckAnalysisQuota('analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94');
|
||||
SELECT CDB_SetUserQuotaInBytes(1);
|
||||
|
||||
52
test/CDB_GhostTables.sql
Normal file
52
test/CDB_GhostTables.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
-- Create user and enable Ghost tables trigger
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
SELECT CDB_EnableGhostTablesTrigger();
|
||||
CREATE ROLE "fulano" LOGIN;
|
||||
GRANT ALL ON SCHEMA cartodb TO "fulano";
|
||||
GRANT SELECT ON cartodb.cdb_ddl_execution TO "fulano";
|
||||
GRANT EXECUTE ON FUNCTION CDB_Username() TO "fulano";
|
||||
GRANT EXECUTE ON FUNCTION CDB_LinkGhostTables(text) TO "fulano";
|
||||
SELECT cartodb.CDB_Conf_SetConf('api_keys_fulano', '{"username": "fulanito", "permissions":[]}');
|
||||
DELETE FROM cdb_conf WHERE key = 'invalidation_service';
|
||||
SET SESSION AUTHORIZATION "fulano";
|
||||
SET client_min_messages TO notice;
|
||||
\set QUIET off
|
||||
|
||||
SELECT CDB_LinkGhostTables(); -- _CDB_LinkGhostTables called (configuration not found)
|
||||
|
||||
-- Add TIS configuration
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT cartodb.CDB_Conf_SetConf('invalidation_service', '{"host": "fake-tis-host", "port": 3142}');
|
||||
SET SESSION AUTHORIZATION "fulano";
|
||||
\set QUIET off
|
||||
|
||||
SELECT CDB_LinkGhostTables(); -- _CDB_LinkGhostTables called
|
||||
|
||||
BEGIN;
|
||||
SELECT to_regclass('cartodb.cdb_ddl_execution'); -- exists
|
||||
SELECT COUNT(*) FROM cartodb.cdb_ddl_execution; -- 0
|
||||
CREATE TABLE tmp(id INT);
|
||||
SELECT COUNT(*) FROM cartodb.cdb_ddl_execution; -- 1
|
||||
END; -- _CDB_LinkGhostTables called
|
||||
|
||||
-- Disable Ghost tables trigger
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT CDB_DisableGhostTablesTrigger();
|
||||
SET SESSION AUTHORIZATION "fulano";
|
||||
\set QUIET off
|
||||
|
||||
SELECT to_regclass('cartodb.cdb_ddl_execution'); -- not exists
|
||||
DROP TABLE tmp; -- _CDB_LinkGhostTables not called
|
||||
|
||||
-- Cleanup
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
REVOKE EXECUTE ON FUNCTION CDB_LinkGhostTables(text) FROM "fulano";
|
||||
REVOKE EXECUTE ON FUNCTION CDB_Username() FROM "fulano";
|
||||
REVOKE ALL ON SCHEMA cartodb FROM "fulano";
|
||||
DROP ROLE "fulano";
|
||||
DELETE FROM cdb_conf WHERE key = 'api_keys_fulano' OR key = 'invalidation_service';
|
||||
\set QUIET off
|
||||
20
test/CDB_GhostTables_expect
Normal file
20
test/CDB_GhostTables_expect
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
WARNING: Invalidation service configuration not found. Skipping Ghost Tables linking.
|
||||
NOTICE: _CDB_LinkGhostTables() called with username=fulanito, event_name=USER
|
||||
|
||||
|
||||
WARNING: Error calling Invalidation Service to link Ghost Tables: Error -2 connecting fake-tis-host:3142. Name or service not known.
|
||||
NOTICE: _CDB_LinkGhostTables() called with username=fulanito, event_name=USER
|
||||
|
||||
BEGIN
|
||||
cdb_ddl_execution
|
||||
0
|
||||
CREATE TABLE
|
||||
1
|
||||
WARNING: Error calling Invalidation Service to link Ghost Tables: Error -2 connecting fake-tis-host:3142. Name or service not known.
|
||||
NOTICE: _CDB_LinkGhostTables() called with username=fulanito, event_name=CREATE TABLE
|
||||
COMMIT
|
||||
|
||||
|
||||
DROP TABLE
|
||||
@@ -1,2 +1,2 @@
|
||||
LINESTRING(4.259 55.858,5.6692453115051 56.0150275120673,7.10720375678704 56.157400475677,8.5718366560563 56.2842986378254,10.0619272412891 56.3949153508462,11.5760785994189 56.4884642014437,13.1127142001617 56.564185934303,14.6700812655504 56.6213555706215,16.2462571744128 56.6592896061102,17.8391590143095 56.6773531596105,19.4465562981665 56.6749669334121,21.0660867567155 56.6516138405427,22.6952750058883 56.6068451534252,24.3315537765309 56.540286032869,25.9722872888145 56.4516403065472,27.6147962622065 56.3406943817481,29.2563839799455 56.207320197769,30.8943627796619 56.0514771479657,32.5260803224591 55.8732129290618,34.1489450028345 55.6726633044968,35.7604499005266 55.4500507979281,37.3581947399686 55.2056823610616,38.9399054089486 54.9399460854786,40.5034506895044 54.6533070499613,42.0468559644411 54.3463024122038,43.5683137754523 54.0195358662507,45.066191217402 53.673671594382,46.5390342525062 53.3094278446298,47.9855691138079 52.9275702630659,49.4047010366934 52.5289051040742,50.7955106088955 52.1142724327331,52.1572480633875 51.6845394219955,53.4893258557794 51.2405938343407,54.7913098701049 50.7833377637432,56.0629095865715 50.3136816997865,57.3039675245588 49.8325389621027,58.5144482465496 49.3408205404538,59.6944271762829 48.8394303639846,60.8440794494795 48.329261012675,61.9636689799149 47.811189874886,63.0535378889196 47.2860757471582,64.1140964137264 46.7547558660385,65.1458133802427 46.2180433565989,66.1492072992903 45.676725078361,67.1248381223566 45.131559846414,68.0732996734468 44.583277003498,68.9952127576034 44.0325753175597,69.8912189338183 43.4801221786776,70.7619749300985 42.9265530691612,71.6081476710291 42.3724712809595,72.4304098829428 41.8184478551838,73.2294362384225 41.2650217194684,74.0059 40.7127)
|
||||
LINESTRING(4.259 55.858,4.96060044865294 55.9382939511593,5.6692453115051 56.0150275120673,6.38482117645567 56.0880973218335,7.10720375678705 56.157400475677,7.83625773770865 56.2228347173136,8.5718366560563 56.2842986378254,9.31378281572326 56.3416918804739,10.0619272412891 56.3949153508462,10.8160896721679 56.4438714316548,11.5760785994189 56.4884642014437,12.3416913471456 56.528599656387,13.1127142001617 56.5641859343031,13.8889225793161 56.5951335399513,14.6700812655504 56.6213555706215,15.4559446734179 56.6427679409819,16.2462571744128 56.6592896061102,17.0407534700619 56.6708427815999,17.8391590143095 56.6773531596105,18.6411904842936 56.6787501197174,19.4465562981665 56.6749669334121,20.2549571781681 56.6659409611101,21.0660867567155 56.6516138405428,21.8796322228404 56.6319316654367,22.6952750058883 56.6068451534252,23.5126914929996 56.5763098021872,24.3315537765309 56.5402860328691,25.1515304272452 56.4987393199198,25.9722872888145 56.4516403065472,26.7934882889404 56.3989649050969,27.6147962622065 56.3406943817482,28.4358737796488 56.2768154250305,29.2563839799456 56.207320197769,30.0759913971174 56.1322063721813,30.8943627796619 56.0514771479657,31.7111678961496 55.9651412533344,32.5260803224592 55.8732129290618,33.3387782060384 55.7757118957345,34.1489450028345 55.6726633044969,34.9562701828379 55.5640976716962,35.7604499005266 55.4500507979282,36.5611876268714 55.3305636720814,37.3581947399687 55.2056823610617,38.1511910717861 55.0754578859583,38.9399054089486 54.9399460854787,39.7240759459355 54.7992074675415,40.5034506895044 54.6533070499613,41.277787813601 54.5023141912026,42.0468559644411 54.3463024122039,42.8104345158644 54.1853492102971,43.5683137754524 54.0195358662508,44.3202951422663 53.8489472454711,45.066191217402 53.6736715943821,45.8058258688602 53.4938003329924,46.5390342525062 53.3094278446299,47.2656627911282 53.1206512637978,47.985569113808 52.9275702630661,48.6986219579803 52.7302868398744,49.4047010366934 52.5289051040743,50.1036968736777 52.3235310669909,50.7955106088955 52.1142724327332,51.4800537772815 51.9012383924278,52.1572480633875 51.6845394219956,52.8270250346284 51.4642870840378,53.4893258557795 51.2405938343408,54.1441009873167 51.0135728334539,54.791309870105 50.7833377637434,55.4309205988438 50.5500026522693,56.0629095865715 50.3136816997866,56.6872612224038 50.0744891161178,57.3039675245588 49.8325389621028,57.9130277905821 49.5879449982851,58.5144482465496 49.3408205404539,59.1082416968843 49.091278322122,59.6944271762829 48.8394303639847,60.2730296051101 48.5853878503721,60.8440794494795 48.3292610126751,61.40761238711 48.0711590197009,61.9636689799149 47.8111898748862,62.5122943541616 47.5494603202768,63.0535378889195 47.2860757471584,63.5874529134047 47.0211401132109,64.1140964137264 46.7547558660387,64.6335287494427 46.4870238729191,65.1458133802426 46.2180433565991,65.6510166029904 45.9479118369597,66.1492072992903 45.6767250783612,66.6404566936622 45.4045770424768,67.1248381223566 45.1315598464143,67.6024268127789 44.8577637259253,68.0732996734467 44.5832770034983,68.5375350943572 44.3081860611283,68.9952127576034 44.0325753175599,69.4464134580461 43.7565272097974,69.8912189338183 43.4801221786779,70.3297117064144 43.2034386583077,70.7619749300985 42.9265530691615,71.1880922503468 42.6495398146493,71.6081476710291 42.3724712809597,72.0222254300221 42.0954178399905,72.4304098829428 41.8184478551841,72.8327853946822 41.5416276900883,73.2294362384225 41.2650217194687,73.6204465018146 40.9886923428039,74.0059 40.7127)
|
||||
LINESTRING(4.259 55.858,5.53349240387128 56.0006659105918,6.81698919498694 56.130094578525,8.10870381314147 56.2461317260662,9.40781156033233 56.3486370295466,10.7134527044527 56.4374849223869,12.0247359780093 56.5125653297878,13.3407424424749 56.573784325367,14.660529681225 56.6210647008095,15.9831362768927 56.654346440595,17.307586522649 56.67358709506,18.6328953115992 56.6787620464102,19.9580731443722 56.6698646638042,21.282131192215 56.6469063452276,22.6040863516019 56.6099164455407,23.922966226566 56.5589420917603,25.2378139766594 56.4940478882858,26.5476929715805 56.4153155163602,27.8516911979552 56.3228432335229,29.1489253693643 56.2167452801302,30.4385446972665 56.0971512011646,31.7197342877491 55.9642050924945,32.9917181368037 55.8180647814723,34.2537617048216 55.6589009522625,35.5051740589896 55.4868962265697,36.7453095800251 55.3022442104976,37.9735692370026 55.1051485181267,39.1894014407465 54.8958217820713,40.3923024922398 54.6744846607816,41.5818166476476 54.4413648517294,42.757535825811 54.1966961188706,43.919098987406 53.9407173419567,45.0661912174019 53.673671594382,46.198542544017 53.3958052553427,47.3159265281308 53.1073671611612,48.4181586571351 52.8086077997244,49.5050945765883 52.499778551104,50.5766281918714 52.1811309766006,51.6326896704254 51.8529161576737,52.673243373185 51.5153840855177,53.6982857415906 51.1687831014009,54.7078431641625 50.8133593873253,55.7019698441171 50.4493565060761,56.6807456869812 50.0770149893128,57.6442742246566 49.6965719720156,58.5926805899637 49.3082608713202,59.5261095533829 48.9123111075629,60.4447236315382 48.5089478652008,61.3487012749643 48.0983918911668,62.2382351408597 47.6808593281578,63.1135304548766 47.2565615803358,63.9748034645285 46.8257052089336,64.822279985501 46.3884918552974,65.6561940410346 45.9451181889661,66.476786593589 45.4957758784676,67.284304367196 45.0406515826125,68.0789987582454 44.5799269601738,68.8611248319107 44.1137786959568,69.6309404010034 43.6423785413868,70.388705183725 43.1658933678633,71.1346800365587 42.6844852312539,71.8691262583921 42.1983114460249,72.5923049618788 41.7075246676227,73.3044765080245 41.2122729818388,74.0059 40.7127)
|
||||
LINESTRING(4.259 55.858,4.89507305967085 55.930977446384,5.53349240387128 56.0006659105918,6.17416348361598 56.0670448594645,6.81698919498694 56.130094578525,7.46186995983655 56.1897961993418,8.10870381314147 56.2461317260662,8.75738649688733 56.2990840610623,9.40781156033233 56.3486370295466,10.0598704664666 56.3947754031591,10.7134527044527 56.4374849223869,11.3684459078018 56.4767523177655,12.0247359780093 56.5125653297878,12.6822072133468 56.5449127274491,13.3407424424749 56.573784325367,14.0002231625192 56.5991709994144,14.660529681225 56.6210647008095,15.3215412627822 56.6394584686143,15.9831362768927 56.654346440595,16.6451923506331 56.6657238624055,17.307586522649 56.67358709506,17.9701953992046 56.677933620668,18.6328953115992 56.6787620464102,19.2955624744544 56.6760721067401,19.9580731443722 56.6698646638042,20.6203037784591 56.6601417060788,21.282131192215 56.6469063452276,21.943432716288 56.6301628111935,22.6040863516019 56.6099164455407,23.2639709223762 56.5861736930735,23.922966226566 56.5589420917603,24.5809531832687 56.5282302610022,25.2378139766594 56.4940478882858,25.8934321960358 56.4564057142701,26.5476929715805 56.4153155163602,27.2004831054654 56.3707900908252,27.8516911979552 56.3228432335229,28.5012077681911 56.2714897192993,29.1489253693643 56.2167452801302,29.7947386980206 56.1586265820819,30.4385446972665 56.0971512011646,31.0802426536785 56.0323375981587,31.7197342877491 55.9642050924945,32.3569238377352 55.8927738352675,32.9917181368037 55.8180647814723,33.6240266834038 55.74009966154,34.2537617048216 55.6589009522625,34.8808382139074 55.5744918471876,35.5051740589896 55.4868962265697,36.1266899670207 55.3961386269571,36.7453095800251 55.3022442104976,37.3609594849451 55.2052387340427,37.9735692370026 55.1051485181267,38.5830713767178 55.0020004158976,39.1894014407465 54.8958217820713,39.7924979667135 54.7866404419798,40.3923024922398 54.6744846607816,40.9887595483734 54.5593831128969,41.5818166476476 54.4413648517294,42.1714242670021 54.3204592797319,42.757535825811 54.1966961188706,43.3401076592708 54.0701053815371,43.919098987406 53.9407173419567,44.4944718799548 53.8085625081347,45.0661912174019 53.673671594382,45.6342246484243 53.5360754944551,46.198542544017 53.3958052553427,46.7591179485663 53.2528920517295,47.3159265281308 53.1073671611612,47.8689465161932 52.9592619399335,48.4181586571351 52.8086077997244,48.9635461476859 52.6554361849853,49.5050945765883 52.499778551104,50.0427918627159 52.3416663433486,50.5766281918714 52.1811309766006,51.1065959524853 52.0182038158815,51.6326896704254 51.8529161576737,52.1549059431199 51.6852992120372,52.673243373185 51.5153840855177,53.1877025017413 51.3432017648431,53.6982857415906 51.1687831014009,54.2049973104167 50.9921587964881,54.7078431641625 50.8133593873253,55.2068309307272 50.6324152338211,55.7019698441171 50.4493565060761,56.1932706791714 50.2642131726125,56.6807456869812 50.0770149893128,57.1644085311015 49.8877914890534,57.6442742246566 49.6965719720156,58.1203590684218 49.5033854966561,58.5926805899637 49.3082608713202,59.0612574839055 49.1112266464775,59.5261095533829 48.9123111075629,59.9872576527434 48.7115422684016,60.4447236315382 48.5089478652008,60.8985302798459 48.3045553510865,61.3487012749643 48.0983918911668,61.7952611294973 47.8904843581013,62.2382351408597 47.6808593281578,62.6776493422177 47.4695430777354,63.1135304548766 47.2565615803358,63.5459058421237 47.0419405039633,63.9748034645285 46.8257052089336,64.4002518367009 46.6078807460742,64.822279985501 46.3884918552974,65.2409174096934 46.1675629645276,65.6561940410346 45.9451181889661,66.0681402067793 45.7211813306754,66.476786593589 45.4957758784676,66.8821642128236 45.2689250080781,67.284304367196 45.0406515826125,67.6832386187654 44.8109781532476,68.0789987582454 44.5799269601738,68.4716167756018 44.3475199337644,68.8611248319107 44.1137786959568,69.2475552324516 43.878724561833,69.6309404010034 43.6423785413868,70.0113128553159 43.4047613414637,70.388705183725 43.1658933678633,70.7631500228809 42.925794727592,71.1346800365587 42.6844852312539,71.5033278955199 42.4419843955718,71.8691262583921 42.1983114460249,72.2321077535378 41.953485319597,72.5923049618788 41.7075246676227,72.9497504006463 41.4604478587259,73.3044765080245 41.2122729818388,73.6565156286596 40.963017849297,74.0059 40.7127)
|
||||
|
||||
@@ -126,3 +126,13 @@ SELECT * FROM cartodb._CDB_Octet_Truncate('piraña', 6);
|
||||
|
||||
-- Test _CDB_Octet_Truncate UTF8 case
|
||||
SELECT * FROM cartodb._CDB_Octet_Truncate('piraña', 7);
|
||||
|
||||
-- Test _CDB_Table_Exists
|
||||
CREATE TABLE public.this_table_exists();
|
||||
SELECT cartodb._CDB_Table_Exists('this_table_does_not_exist');
|
||||
SELECT cartodb._CDB_Table_Exists('this_schema_does_not_exist.this_table_does_not_exist');
|
||||
SELECT cartodb._CDB_Table_Exists('this_table_exists');
|
||||
SELECT cartodb._CDB_Table_Exists('public.this_table_exists');
|
||||
SELECT cartodb._CDB_Table_Exists('raster_overviews'); -- view created by postgis
|
||||
SELECT cartodb._CDB_Table_Exists('public.raster_overviews');
|
||||
DROP TABLE public.this_table_exists
|
||||
|
||||
@@ -57,3 +57,11 @@ DROP TABLE
|
||||
pira
|
||||
pirañ
|
||||
piraña
|
||||
CREATE TABLE
|
||||
f
|
||||
f
|
||||
t
|
||||
t
|
||||
f
|
||||
f
|
||||
DROP TABLE
|
||||
|
||||
@@ -5,9 +5,8 @@ WITH data AS (
|
||||
15.01, 14.99,
|
||||
20.1, 19.9]::numeric[] AS s
|
||||
)
|
||||
-- expectation is: 1, 5, 10, 15, 20
|
||||
-- TODO: fix cdb_jenksbins to match ^^
|
||||
SELECT round(unnest(CDB_JenksBins(s, 5))) FROM data;
|
||||
SELECT unnest(CDB_JenksBins(s, 5)) FROM data;
|
||||
|
||||
|
||||
WITH data_nulls AS (
|
||||
SELECT Array[0.99, 1.0, 1.01,
|
||||
@@ -18,6 +17,20 @@ WITH data_nulls AS (
|
||||
null, null,
|
||||
20.1, 19.9]::numeric[] AS s
|
||||
)
|
||||
-- expectation is: 1, 5, 10, 15, 20
|
||||
-- TODO: fix cdb_jenksbins to match ^^
|
||||
SELECT round(unnest(CDB_JenksBins(s, 5))) FROM data_nulls;
|
||||
SELECT unnest(CDB_JenksBins(s, 5)) FROM data_nulls;
|
||||
|
||||
|
||||
WITH data_inverse AS (
|
||||
SELECT Array[0.99, 1.0, 1.01,
|
||||
4.99, 5.01,
|
||||
10.01, 10.01,
|
||||
15.01, 14.99,
|
||||
20.1, 19.9]::numeric[] AS s
|
||||
)
|
||||
SELECT unnest(CDB_JenksBins(s, 5, 0, true)) FROM data_inverse;
|
||||
|
||||
|
||||
WITH data_small AS (
|
||||
SELECT Array[0.99, 1.0, 10.01, 10.01, 10.01, 10.01]::numeric[] AS s
|
||||
)
|
||||
SELECT unnest(CDB_JenksBins(s, 4)) FROM data_small;
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
1
|
||||
5
|
||||
10
|
||||
20
|
||||
20
|
||||
1
|
||||
5
|
||||
10
|
||||
20
|
||||
|
||||
1.01
|
||||
5.01
|
||||
10.01
|
||||
15.01
|
||||
20.1
|
||||
1.01
|
||||
5.01
|
||||
10.01
|
||||
15.01
|
||||
20.1
|
||||
0.99
|
||||
4.99
|
||||
10.01
|
||||
14.99
|
||||
19.9
|
||||
0.99
|
||||
1.0
|
||||
10.01
|
||||
|
||||
177
test/CDB_OAuth.sql
Normal file
177
test/CDB_OAuth.sql
Normal file
@@ -0,0 +1,177 @@
|
||||
-- Create user and enable OAuth event trigger
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
DROP ROLE IF EXISTS "creator_role";
|
||||
CREATE ROLE "creator_role" LOGIN;
|
||||
DROP ROLE IF EXISTS "ownership_role";
|
||||
CREATE ROLE "ownership_role" LOGIN;
|
||||
GRANT ALL ON SCHEMA cartodb TO "creator_role";
|
||||
SELECT CDB_Conf_SetConf('api_keys_creator_role', '{"username": "creator_role", "permissions":[]}');
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
SET client_min_messages TO notice;
|
||||
\set QUIET off
|
||||
|
||||
-- First part without event trigger
|
||||
|
||||
CREATE TABLE test(id INT);
|
||||
INSERT INTO test VALUES(1);
|
||||
CREATE TABLE test_tablesas AS SELECT * FROM test;
|
||||
CREATE VIEW test_view AS SELECT * FROM test;
|
||||
CREATE MATERIALIZED VIEW test_mview AS SELECT * FROM test;
|
||||
SELECT * INTO test_selectinto FROM test;
|
||||
|
||||
SELECT * FROM test;
|
||||
SELECT * FROM test_tablesas;
|
||||
SELECT * FROM test_view;
|
||||
SELECT * FROM test_mview;
|
||||
SELECT * FROM test_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "ownership_role";
|
||||
\set QUIET off
|
||||
|
||||
SELECT * FROM test;
|
||||
SELECT * FROM test_tablesas;
|
||||
SELECT * FROM test_view;
|
||||
SELECT * FROM test_mview;
|
||||
SELECT * FROM test_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
DROP TABLE test_tablesas;
|
||||
DROP VIEW test_view;
|
||||
DROP MATERIALIZED VIEW test_mview;
|
||||
DROP TABLE test_selectinto;
|
||||
DROP TABLE test;
|
||||
|
||||
-- Second part with event trigger but without ownership_role_name in cdb_conf
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT CDB_EnableOAuthReassignTablesTrigger();
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
CREATE TABLE test2(id INT);
|
||||
INSERT INTO test2 VALUES(1);
|
||||
CREATE TABLE test2_tablesas AS SELECT * FROM test2;
|
||||
CREATE VIEW test2_view AS SELECT * FROM test2;
|
||||
CREATE MATERIALIZED VIEW test2_mview AS SELECT * FROM test2;
|
||||
SELECT * INTO test2_selectinto FROM test2;
|
||||
|
||||
SELECT * FROM test2;
|
||||
SELECT * FROM test2_tablesas;
|
||||
SELECT * FROM test2_view;
|
||||
SELECT * FROM test2_mview;
|
||||
SELECT * FROM test2_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "ownership_role";
|
||||
\set QUIET off
|
||||
|
||||
SELECT * FROM test2;
|
||||
SELECT * FROM test2_tablesas;
|
||||
SELECT * FROM test2_view;
|
||||
SELECT * FROM test2_mview;
|
||||
SELECT * FROM test2_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
DROP TABLE test2_tablesas;
|
||||
DROP VIEW test2_view;
|
||||
DROP MATERIALIZED VIEW test2_mview;
|
||||
DROP TABLE test2_selectinto;
|
||||
DROP TABLE test2;
|
||||
|
||||
-- Third part with event trigger but with empty ownership_role_name in cdb_conf
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT CDB_Conf_SetConf('api_keys_creator_role', '{"username": "creator_role", "permissions":[], "ownership_role_name": ""}');
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
CREATE TABLE test3(id INT);
|
||||
INSERT INTO test3 VALUES(1);
|
||||
CREATE TABLE test3_tablesas AS SELECT * FROM test3;
|
||||
CREATE VIEW test3_view AS SELECT * FROM test3;
|
||||
CREATE MATERIALIZED VIEW test3_mview AS SELECT * FROM test3;
|
||||
SELECT * INTO test3_selectinto FROM test3;
|
||||
|
||||
SELECT * FROM test3;
|
||||
SELECT * FROM test3_tablesas;
|
||||
SELECT * FROM test3_view;
|
||||
SELECT * FROM test3_mview;
|
||||
SELECT * FROM test3_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "ownership_role";
|
||||
\set QUIET off
|
||||
|
||||
SELECT * FROM test3;
|
||||
SELECT * FROM test3_tablesas;
|
||||
SELECT * FROM test3_view;
|
||||
SELECT * FROM test3_mview;
|
||||
SELECT * FROM test3_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
DROP TABLE test3_tablesas;
|
||||
DROP VIEW test3_view;
|
||||
DROP MATERIALIZED VIEW test3_mview;
|
||||
DROP TABLE test3_selectinto;
|
||||
DROP TABLE test3;
|
||||
|
||||
-- Fourth part with the event trigger active and configured
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT CDB_Conf_SetConf('api_keys_creator_role', '{"username": "creator_role", "permissions":[], "ownership_role_name": "ownership_role"}');
|
||||
SET SESSION AUTHORIZATION "creator_role";
|
||||
\set QUIET off
|
||||
|
||||
CREATE TABLE test4(id INT);
|
||||
INSERT INTO test4 VALUES(1);
|
||||
CREATE TABLE test4_tablesas AS SELECT * FROM test4;
|
||||
CREATE VIEW test4_view AS SELECT * FROM test4;
|
||||
CREATE MATERIALIZED VIEW test4_mview AS SELECT * FROM test4;
|
||||
SELECT * INTO test4_selectinto FROM test4;
|
||||
|
||||
SELECT * FROM test4;
|
||||
SELECT * FROM test4_tablesas;
|
||||
SELECT * FROM test4_view;
|
||||
SELECT * FROM test4_mview;
|
||||
SELECT * FROM test4_selectinto;
|
||||
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION "ownership_role";
|
||||
\set QUIET off
|
||||
|
||||
SELECT * FROM test4;
|
||||
SELECT * FROM test4_tablesas;
|
||||
SELECT * FROM test4_view;
|
||||
SELECT * FROM test4_mview;
|
||||
SELECT * FROM test4_selectinto;
|
||||
|
||||
-- Ownership role drops the tables
|
||||
DROP TABLE test4_tablesas;
|
||||
DROP VIEW test4_view;
|
||||
DROP MATERIALIZED VIEW test4_mview;
|
||||
DROP TABLE test4_selectinto;
|
||||
DROP TABLE test4;
|
||||
|
||||
-- Cleanup
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
SELECT CDB_DisableOAuthReassignTablesTrigger();
|
||||
DROP ROLE "ownership_role";
|
||||
REVOKE ALL ON SCHEMA cartodb FROM "creator_role";
|
||||
DROP ROLE "creator_role";
|
||||
DELETE FROM cdb_conf WHERE key = 'api_keys_creator_role';
|
||||
\set QUIET off
|
||||
90
test/CDB_OAuth_expect
Normal file
90
test/CDB_OAuth_expect
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for relation test
|
||||
ERROR: permission denied for relation test_tablesas
|
||||
ERROR: permission denied for relation test_view
|
||||
ERROR: permission denied for relation test_mview
|
||||
ERROR: permission denied for relation test_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
NOTICE: event trigger "oauth_reassign_tables_trigger" does not exist, skipping
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for relation test2
|
||||
ERROR: permission denied for relation test2_tablesas
|
||||
ERROR: permission denied for relation test2_view
|
||||
ERROR: permission denied for relation test2_mview
|
||||
ERROR: permission denied for relation test2_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for relation test3
|
||||
ERROR: permission denied for relation test3_tablesas
|
||||
ERROR: permission denied for relation test3_view
|
||||
ERROR: permission denied for relation test3_mview
|
||||
ERROR: permission denied for relation test3_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
90
test/CDB_OAuth_expect.pg11
Normal file
90
test/CDB_OAuth_expect.pg11
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for table test
|
||||
ERROR: permission denied for table test_tablesas
|
||||
ERROR: permission denied for view test_view
|
||||
ERROR: permission denied for materialized view test_mview
|
||||
ERROR: permission denied for table test_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
NOTICE: event trigger "oauth_reassign_tables_trigger" does not exist, skipping
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for table test2
|
||||
ERROR: permission denied for table test2_tablesas
|
||||
ERROR: permission denied for view test2_view
|
||||
ERROR: permission denied for materialized view test2_mview
|
||||
ERROR: permission denied for table test2_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
ERROR: permission denied for table test3
|
||||
ERROR: permission denied for table test3_tablesas
|
||||
ERROR: permission denied for view test3_view
|
||||
ERROR: permission denied for materialized view test3_mview
|
||||
ERROR: permission denied for table test3_selectinto
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
SELECT 1
|
||||
CREATE VIEW
|
||||
SELECT 1
|
||||
SELECT 1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
DROP TABLE
|
||||
DROP VIEW
|
||||
DROP MATERIALIZED VIEW
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
@@ -2,6 +2,7 @@ SET client_min_messages TO error;
|
||||
\set VERBOSITY default
|
||||
|
||||
\i test/overviews/fixtures.sql
|
||||
vacuum ANALYZE; -- Make sure there are metrics for ST_EstimatedExtent
|
||||
|
||||
SELECT _CDB_Aggregable_Attributes_Expression('base_bare_t'::regclass);
|
||||
SELECT _CDB_Aggregated_Attributes_Expression('base_bare_t'::regclass);
|
||||
|
||||
@@ -6,6 +6,7 @@ INSERT 0 1114
|
||||
CREATE TABLE
|
||||
INSERT 0 5
|
||||
SELECT 1114
|
||||
VACUUM
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ 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_catalog.pg_class}
|
||||
WARNING: CDB_QueryTables cannot explain query: select * from test (42P01: relation "test" does not exist)
|
||||
WARNING: CDB_QueryTables cannot explain query: insert into test values (1) (42P01: relation "test" does not exist)
|
||||
ERROR: relation "test" does not exist
|
||||
WITH a AS (select * from pg_class) select * from a|{pg_catalog.pg_class}
|
||||
CREATE SCHEMA
|
||||
|
||||
94
test/CDB_SyncTableTest.sql
Normal file
94
test/CDB_SyncTableTest.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
-- Setup: create and populate a table to test the syncs
|
||||
\set QUIET on
|
||||
BEGIN;
|
||||
SET client_min_messages TO error;
|
||||
CREATE TABLE test_sync_source (
|
||||
cartodb_id bigint,
|
||||
lat double precision,
|
||||
lon double precision,
|
||||
name text
|
||||
);
|
||||
INSERT INTO test_sync_source VALUES
|
||||
(1, 1.0, 1.0, 'foo'),
|
||||
(2, 2.0, 2.0, 'bar'),
|
||||
(3, 3.0, 3.0, 'patata'),
|
||||
(4, 4.0, 4.0, 'melon');
|
||||
SET client_min_messages TO notice;
|
||||
\set QUIET off
|
||||
|
||||
|
||||
\echo 'First table sync: it should be simply just copied to the destination'
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest');
|
||||
|
||||
\echo 'Next table sync: there shall be no changes'
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest');
|
||||
|
||||
\echo 'Remove a row from the source and check it is deleted from the dest table'
|
||||
DELETE FROM test_sync_source WHERE cartodb_id = 3;
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest');
|
||||
|
||||
\echo 'Insert a new row and check that it is inserted in the dest table'
|
||||
INSERT INTO test_sync_source VALUES (5, 5.0, 5.0, 'sandia');
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest');
|
||||
|
||||
\echo 'Modify row and check that it is modified in the dest table'
|
||||
UPDATE test_sync_source SET name = 'cantaloupe' WHERE cartodb_id = 4;
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest');
|
||||
|
||||
\echo 'Sanity check: the end result is the same source table'
|
||||
SELECT * FROM test_sync_source ORDER BY cartodb_id;
|
||||
SELECT * FROM test_sync_dest ORDER BY cartodb_id;
|
||||
|
||||
|
||||
\echo 'It shall exclude geom columns if instructed to do so'
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
SELECT cartodb.CDB_SetUserQuotaInBytes(0); -- Set user quota to infinite
|
||||
SELECT cartodb.CDB_CartodbfyTable('test_sync_source');
|
||||
SELECT cartodb.CDB_CartodbfyTable('test_sync_dest');
|
||||
UPDATE test_sync_dest SET the_geom = cartodb.CDB_LatLng(lat, lon); -- A "gecoding"
|
||||
\set QUIET off
|
||||
SET client_min_messages TO notice;
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest', '{the_geom, the_geom_webmercator}');
|
||||
SELECT * FROM test_sync_source ORDER BY cartodb_id;
|
||||
SELECT * FROM test_sync_dest ORDER BY cartodb_id;
|
||||
|
||||
\echo 'It will work with schemas that need quoting'
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
CREATE SCHEMA "sch-ema";
|
||||
CREATE TABLE "test_sync_source2" AS SELECT * FROM test_sync_source;
|
||||
\set QUIET off
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source2', 'sch-ema', 'test_sync_dest');
|
||||
INSERT INTO test_sync_source2(cartodb_id, lat, lon, name) VALUES (6, 6.0, 6.0, 'papaya');
|
||||
DELETE FROM test_sync_source2 WHERE cartodb_id = 4;
|
||||
UPDATE test_sync_source2 SET lat = 2.5 WHERE cartodb_id = 2;
|
||||
SET client_min_messages TO notice;
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source2', 'sch-ema', 'test_sync_dest');
|
||||
|
||||
\echo 'It will work with table names that need quoting'
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
CREATE TABLE "test-sync-source" AS SELECT * FROM test_sync_source;
|
||||
\set QUIET off
|
||||
SELECT cartodb.CDB_SyncTable('test-sync-source', 'public', 'test-sync-dest');
|
||||
INSERT INTO "test-sync-source"(cartodb_id, lat, lon, name) VALUES (6, 6.0, 6.0, 'papaya');
|
||||
DELETE FROM "test-sync-source" WHERE cartodb_id = 4;
|
||||
UPDATE "test-sync-source" SET lat = 2.5 WHERE cartodb_id = 2;
|
||||
SET client_min_messages TO notice;
|
||||
SELECT cartodb.CDB_SyncTable('test-sync-source', 'public', 'test-sync-dest');
|
||||
|
||||
\echo 'It will work with column names that need quoting'
|
||||
\set QUIET on
|
||||
SET client_min_messages TO error;
|
||||
ALTER TABLE test_sync_source ADD COLUMN "a-column" int;
|
||||
\set QUIET off
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest2');
|
||||
INSERT INTO test_sync_source(cartodb_id, lat, lon, name) VALUES (6, 6.0, 6.0, 'papaya');
|
||||
DELETE FROM test_sync_source WHERE cartodb_id = 4;
|
||||
UPDATE test_sync_source SET lat = 2.5 WHERE cartodb_id = 2;
|
||||
SET client_min_messages TO notice;
|
||||
SELECT cartodb.CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest2');
|
||||
|
||||
-- Cleanup
|
||||
ROLLBACK;
|
||||
94
test/CDB_SyncTableTest_expect
Normal file
94
test/CDB_SyncTableTest_expect
Normal file
@@ -0,0 +1,94 @@
|
||||
First table sync: it should be simply just copied to the destination
|
||||
NOTICE: INSERTED 4 row(s)
|
||||
|
||||
Next table sync: there shall be no changes
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: DELETED 0 row(s)
|
||||
NOTICE: INSERTED 0 row(s)
|
||||
NOTICE: MODIFIED 0 row(s)
|
||||
|
||||
Remove a row from the source and check it is deleted from the dest table
|
||||
DELETE 1
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: DELETED 1 row(s)
|
||||
NOTICE: INSERTED 0 row(s)
|
||||
NOTICE: MODIFIED 0 row(s)
|
||||
|
||||
Insert a new row and check that it is inserted in the dest table
|
||||
INSERT 0 1
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: DELETED 0 row(s)
|
||||
NOTICE: INSERTED 1 row(s)
|
||||
NOTICE: MODIFIED 0 row(s)
|
||||
|
||||
Modify row and check that it is modified in the dest table
|
||||
UPDATE 1
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: DELETED 0 row(s)
|
||||
NOTICE: INSERTED 0 row(s)
|
||||
NOTICE: MODIFIED 1 row(s)
|
||||
|
||||
Sanity check: the end result is the same source table
|
||||
1|1|1|foo
|
||||
2|2|2|bar
|
||||
4|4|4|cantaloupe
|
||||
5|5|5|sandia
|
||||
1|1|1|foo
|
||||
2|2|2|bar
|
||||
4|4|4|cantaloupe
|
||||
5|5|5|sandia
|
||||
It shall exclude geom columns if instructed to do so
|
||||
0
|
||||
test_sync_source
|
||||
test_sync_dest
|
||||
SET
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: cdb_invalidate_varnish(public.test_sync_dest) called
|
||||
NOTICE: DELETED 0 row(s)
|
||||
NOTICE: cdb_invalidate_varnish(public.test_sync_dest) called
|
||||
NOTICE: INSERTED 0 row(s)
|
||||
NOTICE: cdb_invalidate_varnish(public.test_sync_dest) called
|
||||
NOTICE: MODIFIED 0 row(s)
|
||||
|
||||
1|||1|1|foo
|
||||
2|||2|2|bar
|
||||
4|||4|4|cantaloupe
|
||||
5|||5|5|sandia
|
||||
1|0101000020E6100000000000000000F03F000000000000F03F|0101000020110F0000DB0B4ADA772DFB402B432E49D22DFB40|1|1|foo
|
||||
2|0101000020E610000000000000000000400000000000000040|0101000020110F00003C0C4ADA772D0B4177F404ABE12E0B41|2|2|bar
|
||||
4|0101000020E610000000000000000010400000000000001040|0101000020110F00003C0C4ADA772D1B4160AB497020331B41|4|4|cantaloupe
|
||||
5|0101000020E610000000000000000014400000000000001440|0101000020110F000099476EE86AFC20413E7EB983F2012141|5|5|sandia
|
||||
It will work with schemas that need quoting
|
||||
|
||||
INSERT 0 1
|
||||
DELETE 1
|
||||
UPDATE 1
|
||||
SET
|
||||
NOTICE: relation "test_sync_dest" already exists, skipping
|
||||
NOTICE: DELETED 1 row(s)
|
||||
NOTICE: INSERTED 1 row(s)
|
||||
NOTICE: MODIFIED 1 row(s)
|
||||
|
||||
It will work with table names that need quoting
|
||||
|
||||
INSERT 0 1
|
||||
DELETE 1
|
||||
UPDATE 1
|
||||
SET
|
||||
NOTICE: relation "test-sync-dest" already exists, skipping
|
||||
NOTICE: DELETED 1 row(s)
|
||||
NOTICE: INSERTED 1 row(s)
|
||||
NOTICE: MODIFIED 1 row(s)
|
||||
|
||||
It will work with column names that need quoting
|
||||
|
||||
INSERT 0 1
|
||||
DELETE 1
|
||||
UPDATE 1
|
||||
SET
|
||||
NOTICE: relation "test_sync_dest2" already exists, skipping
|
||||
NOTICE: DELETED 1 row(s)
|
||||
NOTICE: INSERTED 1 row(s)
|
||||
NOTICE: MODIFIED 1 row(s)
|
||||
|
||||
ROLLBACK
|
||||
@@ -1,4 +1,18 @@
|
||||
CREATE ROLE publicuser LOGIN;
|
||||
SET client_min_messages TO ERROR;
|
||||
DO
|
||||
$do$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT *
|
||||
FROM pg_catalog.pg_user
|
||||
WHERE usename = 'publicuser') THEN
|
||||
|
||||
CREATE ROLE publicuser LOGIN;
|
||||
END IF;
|
||||
END
|
||||
$do$;
|
||||
SET client_min_messages TO NOTICE;
|
||||
|
||||
CREATE TABLE pub(a int);
|
||||
CREATE TABLE prv(a int);
|
||||
GRANT SELECT ON TABLE pub TO publicuser;
|
||||
@@ -16,4 +30,3 @@ SELECT 'private_publicuser',CDB_UserTables('private') ORDER BY 2;
|
||||
\c contrib_regression postgres
|
||||
DROP TABLE pub;
|
||||
DROP TABLE prv;
|
||||
DROP ROLE publicuser;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
CREATE ROLE
|
||||
SET
|
||||
DO
|
||||
SET
|
||||
CREATE TABLE
|
||||
CREATE TABLE
|
||||
GRANT
|
||||
@@ -15,4 +17,3 @@ public_publicuser|pub
|
||||
You are now connected to database "contrib_regression" as user "postgres".
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP ROLE
|
||||
|
||||
23
test/CDB_Username.sql
Normal file
23
test/CDB_Username.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
SELECT session_user; -- postgres
|
||||
SELECT CDB_Username(); -- (NULL)
|
||||
|
||||
-- Add the role fulano with api_key and connect with it
|
||||
\set QUIET on
|
||||
CREATE ROLE fulano LOGIN;
|
||||
GRANT USAGE ON SCHEMA cartodb TO fulano;
|
||||
GRANT EXECUTE ON FUNCTION CDB_Username() TO fulano;
|
||||
INSERT INTO cdb_conf (key, value) VALUES ('api_keys_fulano', '{"username": "fulanito", "permissions":[]}');
|
||||
SET SESSION AUTHORIZATION fulano;
|
||||
\set QUIET off
|
||||
|
||||
SELECT session_user; -- fulano
|
||||
SELECT CDB_Username(); -- fulanito
|
||||
|
||||
-- Remove fulano
|
||||
\set QUIET on
|
||||
SET SESSION AUTHORIZATION postgres;
|
||||
REVOKE USAGE ON SCHEMA cartodb FROM fulano;
|
||||
REVOKE EXECUTE ON FUNCTION CDB_Username() FROM fulano;
|
||||
DROP ROLE fulano;
|
||||
DELETE FROM cdb_conf WHERE key = 'api_keys_fulano';
|
||||
\set QUIET off
|
||||
4
test/CDB_Username_expect
Normal file
4
test/CDB_Username_expect
Normal file
@@ -0,0 +1,4 @@
|
||||
postgres
|
||||
|
||||
fulano
|
||||
fulanito
|
||||
@@ -6,6 +6,14 @@ Example, to add a test for CDB_Something function, you'd add:
|
||||
- CDB_SomethingTest.sql
|
||||
- CDB_SomethingTest_expect
|
||||
|
||||
In case you need multiple expects of a test for different versions you have
|
||||
to add .pg$(VERSION) at the end of the file.
|
||||
|
||||
For example if you want an expect file for PG11 you need to have two expect files:
|
||||
|
||||
- CDB_SomethingTest_expect
|
||||
- CDB_SomethingTest_expect.pg11
|
||||
|
||||
To easy the generation of the expected file you can initially omit it,
|
||||
then run "make -C .. installcheck" from the top-level dir and copy
|
||||
../results/test/CDB_SomethingTest.out to CDB_SomethingTest_expect chopping
|
||||
|
||||
@@ -13,7 +13,6 @@ DATABASE=test_extension
|
||||
CMD='echo psql'
|
||||
CMD=psql
|
||||
SED=sed
|
||||
PG_PARALLEL=$(pg_config --version | awk '{$2*=1000; if ($2 >= 9600) print 1; else print 0;}' 2> /dev/null || echo 0)
|
||||
|
||||
OK=0
|
||||
PARTIALOK=0
|
||||
@@ -28,30 +27,6 @@ function clear_partial_result() {
|
||||
PARTIALOK=0
|
||||
}
|
||||
|
||||
function load_sql_file() {
|
||||
if [[ $PG_PARALLEL -eq 0 ]]
|
||||
then
|
||||
tmp_file=/tmp/$(basename $1)_no_parallel
|
||||
${SED} $1 -e 's/PARALLEL \= [A-Z]*/''/g' -e 's/PARALLEL [A-Z]*/''/g' > $tmp_file
|
||||
${CMD} -d ${DATABASE} -f $tmp_file
|
||||
rm $tmp_file
|
||||
else
|
||||
${CMD} -d ${DATABASE} -f $1
|
||||
fi
|
||||
}
|
||||
|
||||
function load_sql_file_schema() {
|
||||
if [[ $PG_PARALLEL -eq 0 ]]
|
||||
then
|
||||
tmp_file=/tmp/$(basename $1)_no_parallel
|
||||
${SED} $1 -e 's/PARALLEL \= [A-Z]*/''/g' -e 's/PARALLEL [A-Z]*/''/g' > $tmp_file
|
||||
PGOPTIONS="$PGOPTIONS --search_path=\"$2\"" ${CMD} -d ${DATABASE} -f $tmp_file
|
||||
rm $tmp_file
|
||||
else
|
||||
PGOPTIONS="$PGOPTIONS --search_path=\"$2\"" ${CMD} -d ${DATABASE} -f $1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function sql() {
|
||||
local ROLE
|
||||
@@ -143,12 +118,15 @@ function create_role_and_schema() {
|
||||
sql "CREATE ROLE ${ROLE} LOGIN;"
|
||||
sql "GRANT CONNECT ON DATABASE \"${DATABASE}\" TO ${ROLE};"
|
||||
sql "CREATE SCHEMA ${ROLE} AUTHORIZATION ${ROLE};"
|
||||
sql "GRANT USAGE ON SCHEMA cartodb TO ${ROLE};"
|
||||
sql "SELECT cartodb.CDB_Organization_Create_Member('${ROLE}');"
|
||||
sql "ALTER ROLE ${ROLE} SET search_path TO ${ROLE},cartodb,public;"
|
||||
}
|
||||
|
||||
|
||||
function drop_role_and_schema() {
|
||||
local ROLE=$1
|
||||
sql "REVOKE USAGE ON SCHEMA cartodb FROM ${ROLE};"
|
||||
sql "DROP SCHEMA \"${ROLE}\" CASCADE;"
|
||||
sql "REVOKE CONNECT ON DATABASE \"${DATABASE}\" FROM \"${ROLE}\";"
|
||||
sql "DROP ROLE \"${ROLE}\";"
|
||||
@@ -200,21 +178,17 @@ function drop_raster_table() {
|
||||
|
||||
function setup_database() {
|
||||
${CMD} -c "CREATE DATABASE ${DATABASE}"
|
||||
sql "CREATE SCHEMA cartodb;"
|
||||
sql "GRANT USAGE ON SCHEMA cartodb TO public;"
|
||||
sql "CREATE EXTENSION postgis;"
|
||||
sql "CREATE EXTENSION plpythonu;"
|
||||
|
||||
log_info "########################### BOOTSTRAP ###########################"
|
||||
load_sql_file scripts-available/CDB_Organizations.sql
|
||||
load_sql_file scripts-available/CDB_OverviewsSupport.sql
|
||||
load_sql_file scripts-available/CDB_AnalysisSupport.sql
|
||||
|
||||
load_sql_file_schema scripts-available/CDB_Quota.sql cartodb
|
||||
load_sql_file_schema scripts-available/CDB_TableMetadata.sql cartodb
|
||||
load_sql_file_schema scripts-available/CDB_ColumnNames.sql cartodb
|
||||
load_sql_file_schema scripts-available/CDB_ColumnType.sql cartodb
|
||||
load_sql_file_schema scripts-available/CDB_AnalysisCatalog.sql cartodb
|
||||
sql postgres "DO
|
||||
\$\$
|
||||
BEGIN
|
||||
IF substring(postgis_lib_version() FROM 1 FOR 1) = '3' THEN
|
||||
CREATE EXTENSION postgis_raster;
|
||||
END IF;
|
||||
END
|
||||
\$\$;"
|
||||
sql "CREATE EXTENSION cartodb CASCADE;"
|
||||
${CMD} -c "ALTER DATABASE ${DATABASE} SET search_path = public, cartodb;"
|
||||
}
|
||||
|
||||
function setup() {
|
||||
@@ -258,7 +232,6 @@ function tear_down() {
|
||||
sql 'DROP ROLE cdb_testmember_2;'
|
||||
|
||||
tear_down_database
|
||||
DATABASE=postgres sql postgres 'DROP ROLE IF EXISTS publicuser';
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +250,7 @@ function run_tests() {
|
||||
else
|
||||
TESTS=`cat $0 | perl -n -e'/function (test.*)\(\)/ && print "$1\n"'`
|
||||
fi
|
||||
setup
|
||||
for t in ${TESTS}
|
||||
do
|
||||
echo "####################################################################"
|
||||
@@ -284,15 +258,15 @@ function run_tests() {
|
||||
echo "# Running: ${t}"
|
||||
echo "#"
|
||||
echo "####################################################################"
|
||||
|
||||
clear_partial_result
|
||||
setup
|
||||
eval ${t}
|
||||
if [[ ${PARTIALOK} -ne 0 ]]
|
||||
then
|
||||
FAILED_TESTS+=(${t})
|
||||
fi
|
||||
tear_down
|
||||
done
|
||||
tear_down
|
||||
if [[ ${OK} -ne 0 ]]
|
||||
then
|
||||
echo
|
||||
@@ -333,20 +307,20 @@ function test_quota_for_each_user() {
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch() {
|
||||
sql "CREATE TABLE touch_example (a int)"
|
||||
sql postgres "CREATE TABLE touch_example (a int)"
|
||||
sql postgres "SELECT updated_at FROM CDB_TableMetadata WHERE tabname = 'touch_example'::regclass;" should ''
|
||||
sql "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT updated_at FROM CDB_TableMetadata WHERE tabname = 'touch_example'::regclass;" should-not ''
|
||||
|
||||
# Another call doesn't fail
|
||||
sql "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT updated_at FROM CDB_TableMetadata WHERE tabname = 'touch_example'::regclass;" should-not ''
|
||||
|
||||
# Works with qualified tables
|
||||
sql "SELECT CDB_TableMetadataTouch('public.touch_example');"
|
||||
sql "SELECT CDB_TableMetadataTouch('public.\"touch_example\"');"
|
||||
sql "SELECT CDB_TableMetadataTouch('\"public\".touch_example');"
|
||||
sql "SELECT CDB_TableMetadataTouch('\"public\".\"touch_example\"');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('public.touch_example');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('public.\"touch_example\"');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('\"public\".touch_example');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('\"public\".\"touch_example\"');"
|
||||
|
||||
# Works with OID
|
||||
sql postgres "SELECT tabname from CDB_TableMetadata;" should 'touch_example'
|
||||
@@ -354,25 +328,25 @@ function test_cdb_tablemetadatatouch() {
|
||||
TABLE_OID=`${CMD} -U postgres ${DATABASE} -c "SELECT attrelid FROM pg_attribute WHERE attrelid = 'touch_example'::regclass limit 1;" -A -t`
|
||||
|
||||
# quoted OID works
|
||||
sql "SELECT CDB_TableMetadataTouch('${TABLE_OID}');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('${TABLE_OID}');"
|
||||
sql postgres "SELECT tabname from CDB_TableMetadata;" should 'touch_example'
|
||||
sql postgres "SELECT count(*) from CDB_TableMetadata;" should 1
|
||||
|
||||
# non quoted OID works
|
||||
sql "SELECT CDB_TableMetadataTouch(${TABLE_OID});"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch(${TABLE_OID});"
|
||||
sql postgres "SELECT tabname from CDB_TableMetadata;" should 'touch_example'
|
||||
sql postgres "SELECT count(*) from CDB_TableMetadata;" should 1
|
||||
|
||||
#### test tear down
|
||||
sql 'DROP TABLE touch_example;'
|
||||
sql postgres 'DROP TABLE touch_example;'
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch_fails_for_unexistent_table() {
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('unexistent_example');" fails
|
||||
sql cdb_testmember_1 "SELECT CDB_TableMetadataTouch('unexistent_example');" fails
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch_fails_from_user_without_permission() {
|
||||
sql "CREATE TABLE touch_example (a int);"
|
||||
sql postgres "CREATE TABLE touch_example (a int);"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
|
||||
sql cdb_testmember_1 "SELECT CDB_TableMetadataTouch('touch_example');" fails
|
||||
@@ -381,6 +355,9 @@ function test_cdb_tablemetadatatouch_fails_from_user_without_permission() {
|
||||
sql cdb_testmember_1 "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
|
||||
sql postgres "REVOKE ALL ON CDB_TableMetadata FROM cdb_testmember_1;"
|
||||
|
||||
#### test tear down
|
||||
sql postgres 'DROP TABLE touch_example;'
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch_fully_qualifies_names() {
|
||||
@@ -418,6 +395,36 @@ function test_cdb_tablemetadatatouch_fully_qualifies_names() {
|
||||
sql postgres 'DROP TABLE touch_invalidations'
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadata_text() {
|
||||
|
||||
#create and touch tables
|
||||
sql postgres "CREATE TABLE touch_ex_a (id int);"
|
||||
sql postgres "CREATE TABLE touch_ex_b (id int);"
|
||||
sql postgres "CREATE TABLE touch_ex_c (id int);"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_ex_a');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_ex_b');"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_ex_c');"
|
||||
|
||||
#ensure there is 1 record per table
|
||||
QUERY="SELECT COUNT(1) FROM (SELECT 1 FROM cdb_tablemetadata_text "
|
||||
QUERY+="GROUP BY tabname HAVING COUNT(1) > 1) s;"
|
||||
sql postgres "$QUERY" should "0"
|
||||
|
||||
#ensure timestamps are distinct and properly ordered
|
||||
QUERY="SELECT (SELECT updated_at FROM CDB_TableMetadata_Text WHERE tabname='public.touch_ex_a')"
|
||||
QUERY+=" < (SELECT updated_at FROM CDB_TableMetadata_Text WHERE tabname='public.touch_ex_b');"
|
||||
sql postgres "$QUERY" should "t"
|
||||
QUERY="SELECT (SELECT updated_at FROM CDB_TableMetadata_Text WHERE tabname='public.touch_ex_b')"
|
||||
QUERY+=" < (SELECT updated_at FROM CDB_TableMetadata_Text WHERE tabname='public.touch_ex_c');"
|
||||
sql postgres "$QUERY" should "t"
|
||||
|
||||
#cleanup
|
||||
sql postgres "DROP TABLE touch_ex_a;"
|
||||
sql postgres "DROP TABLE touch_ex_b;"
|
||||
sql postgres "DROP TABLE touch_ex_c;"
|
||||
|
||||
}
|
||||
|
||||
function test_cdb_column_names() {
|
||||
sql cdb_testmember_1 'CREATE TABLE cdb_testmember_1.table_cnames(c int, a int, r int, t int, o int);'
|
||||
sql cdb_testmember_2 'CREATE TABLE cdb_testmember_2.table_cnames(d int, b int);'
|
||||
@@ -453,9 +460,6 @@ function test_cdb_column_type() {
|
||||
}
|
||||
|
||||
function test_cdb_querytables_schema_and_table_names_with_dots() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file scripts-available/CDB_QueryTables.sql
|
||||
|
||||
sql postgres 'CREATE SCHEMA "foo.bar";'
|
||||
sql postgres 'CREATE TABLE "foo.bar"."c.a.r.t.o.d.b" (a int);'
|
||||
sql postgres 'INSERT INTO "foo.bar"."c.a.r.t.o.d.b" values (1);'
|
||||
@@ -469,9 +473,6 @@ function test_cdb_querytables_schema_and_table_names_with_dots() {
|
||||
}
|
||||
|
||||
function test_cdb_querytables_table_name_with_dots() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file scripts-available/CDB_QueryTables.sql
|
||||
|
||||
sql postgres 'CREATE TABLE "w.a.d.u.s" (a int);';
|
||||
|
||||
sql postgres 'SELECT CDB_QueryTablesText($q$select * from "w.a.d.u.s"$q$);' should '{"public.\"w.a.d.u.s\""}'
|
||||
@@ -481,9 +482,6 @@ function test_cdb_querytables_table_name_with_dots() {
|
||||
}
|
||||
|
||||
function test_cdb_querytables_happy_cases() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file scripts-available/CDB_QueryTables.sql
|
||||
|
||||
sql postgres 'CREATE TABLE wadus (a int);';
|
||||
sql postgres 'CREATE TABLE "FOOBAR" (a int);';
|
||||
sql postgres 'CREATE SCHEMA foo;'
|
||||
@@ -505,18 +503,8 @@ function test_cdb_querytables_happy_cases() {
|
||||
}
|
||||
|
||||
function test_foreign_tables() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file scripts-available/CDB_QueryTables.sql
|
||||
load_sql_file scripts-available/CDB_TableMetadata.sql
|
||||
load_sql_file scripts-available/CDB_Conf.sql
|
||||
load_sql_file scripts-available/CDB_ForeignTable.sql
|
||||
|
||||
|
||||
DATABASE=fdw_target setup_database
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file scripts-available/CDB_QueryTables.sql
|
||||
load_sql_file scripts-available/CDB_TableMetadata.sql
|
||||
|
||||
DATABASE=fdw_target sql postgres "DO
|
||||
\$\$
|
||||
BEGIN
|
||||
@@ -529,6 +517,7 @@ BEGIN
|
||||
END IF;
|
||||
END
|
||||
\$\$;"
|
||||
|
||||
DATABASE=fdw_target sql postgres 'CREATE SCHEMA test_fdw;'
|
||||
DATABASE=fdw_target sql postgres 'CREATE TABLE test_fdw.foo (a int);'
|
||||
DATABASE=fdw_target sql postgres 'INSERT INTO test_fdw.foo (a) values (42);'
|
||||
@@ -543,20 +532,19 @@ END
|
||||
DATABASE=fdw_target sql postgres "SELECT cdb_tablemetadatatouch('test_fdw.foo'::regclass);"
|
||||
DATABASE=fdw_target sql postgres "SELECT cdb_tablemetadatatouch('test_fdw.foo2'::regclass);"
|
||||
|
||||
sql postgres "SELECT cartodb.CDB_Conf_SetConf('fdws', '{\"test_fdw\": {\"server\": {\"host\": \"localhost\", \"dbname\": \"fdw_target\"},
|
||||
# Add PGPORT to conf if it is set
|
||||
PORT_SPEC=""
|
||||
if [[ "$PGPORT" != "" ]] ; then
|
||||
PORT_SPEC=", \"port\": \"$PGPORT\""
|
||||
fi
|
||||
sql postgres "SELECT cartodb.CDB_Conf_SetConf('fdws', '{\"test_fdw\": {\"server\": {\"host\": \"localhost\", \"dbname\": \"fdw_target\" $PORT_SPEC },
|
||||
\"users\": {\"public\": {\"user\": \"fdw_user\", \"password\": \"foobarino\"}}}}')"
|
||||
|
||||
sql postgres "SELECT cartodb._CDB_Setup_FDW('test_fdw')"
|
||||
|
||||
sql postgres "SHOW server_version_num"
|
||||
if [ "$RESULT" -gt 90499 ]
|
||||
then
|
||||
sql postgres "SELECT cartodb.CDB_Add_Remote_Table('test_fdw', 'foo')"
|
||||
sql postgres "SELECT * from test_fdw.foo;"
|
||||
else
|
||||
echo "NOTICE: PostgreSQL version is less than 9.5 ($RESULT). Skipping CDB_Add_Remote_Table."
|
||||
sql postgres "CREATE FOREIGN TABLE test_fdw.foo (a int) SERVER test_fdw OPTIONS (table_name 'foo', schema_name 'test_fdw')"
|
||||
fi
|
||||
sql postgres "SELECT cartodb.CDB_Add_Remote_Table('test_fdw', 'foo')"
|
||||
sql postgres "SELECT * from test_fdw.foo;"
|
||||
|
||||
|
||||
sql postgres "SELECT n.nspname,
|
||||
c.relname,
|
||||
@@ -598,6 +586,73 @@ test_extension|public|"local-table-with-dashes"'
|
||||
sql postgres "SELECT cartodb.CDB_Last_Updated_Time(ARRAY['test_extension.public.\"local-table-with-dashes\"']::text[]) < now()" should 't'
|
||||
sql postgres "SELECT cartodb.CDB_Last_Updated_Time(ARRAY['test_extension.public.\"local-table-with-dashes\"']::text[]) > (now() - interval '1 minute')" should 't'
|
||||
|
||||
# Check CDB_Get_Foreign_Updated_At is robust to unimported CDB_TableMetadata
|
||||
sql postgres "DROP FOREIGN TABLE IF EXISTS test_fdw.cdb_tablemetadata;"
|
||||
sql postgres "SELECT cartodb.CDB_Get_Foreign_Updated_At('test_fdw.foo') IS NULL" should 't'
|
||||
|
||||
|
||||
# Check user-defined FDW's
|
||||
# Set up a user foreign server
|
||||
read -d '' ufdw_config <<- EOF
|
||||
{
|
||||
"server": {
|
||||
"extensions": "postgis",
|
||||
"dbname": "fdw_target",
|
||||
"host": "localhost",
|
||||
"port": ${PGPORT:-5432}
|
||||
},
|
||||
"user_mapping": {
|
||||
"user": "fdw_user",
|
||||
"password": "foobarino"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sql postgres "SELECT cartodb._CDB_SetUp_User_PG_FDW_Server('user-defined-test', '$ufdw_config');"
|
||||
|
||||
# Grant a user access to that FDW, and to grant to others
|
||||
sql postgres 'GRANT "cdb_fdw_user-defined-test" TO cdb_testmember_1 WITH ADMIN OPTION;'
|
||||
|
||||
# Set up a user foreign table
|
||||
sql cdb_testmember_1 "SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('user-defined-test', 'test_fdw', 'foo');"
|
||||
|
||||
# Check that the table can be accessed by the owner/creator
|
||||
sql cdb_testmember_1 'SELECT * from "cdb_fdw_user-defined-test".foo;'
|
||||
sql cdb_testmember_1 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' should 42
|
||||
|
||||
# Check that a role with no permissions cannot use the FDW to access a remote table
|
||||
sql cdb_testmember_2 'IMPORT FOREIGN SCHEMA test_fdw LIMIT TO (foo) FROM SERVER "cdb_fdw_user-defined-test" INTO public' fails
|
||||
|
||||
# Check that the table can be accessed by some other user by granting the role
|
||||
sql cdb_testmember_2 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' fails
|
||||
sql cdb_testmember_1 'GRANT "cdb_fdw_user-defined-test" TO cdb_testmember_2;'
|
||||
sql cdb_testmember_2 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' should 42
|
||||
sql cdb_testmember_1 'REVOKE "cdb_fdw_user-defined-test" FROM cdb_testmember_2;'
|
||||
|
||||
# Check that the table can be accessed by org members
|
||||
sql cdb_testmember_2 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' fails
|
||||
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Grant_Role('cdb_fdw_user-defined-test');"
|
||||
sql cdb_testmember_2 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' should 42
|
||||
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Revoke_Role('cdb_fdw_user-defined-test');"
|
||||
|
||||
# By default publicuser cannot access the FDW
|
||||
sql publicuser 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' fails
|
||||
sql cdb_testmember_1 'GRANT "cdb_fdw_user-defined-test" TO publicuser;' # but can be granted
|
||||
sql publicuser 'SELECT a from "cdb_fdw_user-defined-test".foo LIMIT 1;' should 42
|
||||
sql cdb_testmember_1 'REVOKE "cdb_fdw_user-defined-test" FROM publicuser;'
|
||||
|
||||
# If there are dependent objects, we cannot drop the foreign server
|
||||
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('user-defined-test')" fails
|
||||
sql cdb_testmember_1 'DROP FOREIGN TABLE "cdb_fdw_user-defined-test".foo;'
|
||||
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('user-defined-test')"
|
||||
|
||||
# But if there are, we can set the force flag to true to drop everything (defaults to false)
|
||||
sql postgres "SELECT cartodb._CDB_SetUp_User_PG_FDW_Server('another_user_defined_test', '$ufdw_config');"
|
||||
sql postgres 'GRANT cdb_fdw_another_user_defined_test TO cdb_testmember_1 WITH ADMIN OPTION;'
|
||||
sql cdb_testmember_1 "SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('another_user_defined_test', 'test_fdw', 'foo');"
|
||||
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('another_user_defined_test', /* force = */ true)"
|
||||
|
||||
|
||||
# Teardown
|
||||
DATABASE=fdw_target sql postgres 'REVOKE USAGE ON SCHEMA test_fdw FROM fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'REVOKE SELECT ON test_fdw.foo FROM fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'REVOKE SELECT ON test_fdw.foo2 FROM fdw_user;'
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
DATABASE=test_organizations
|
||||
CMD=psql
|
||||
SED=sed
|
||||
PG_PARALLEL=$(pg_config --version | awk '{$2*=1000; if ($2 >= 9600) print 1; else print 0;}' 2> /dev/null || echo 0)
|
||||
|
||||
OK=0
|
||||
PARTIALOK=0
|
||||
@@ -25,19 +24,6 @@ function clear_partial_result() {
|
||||
PARTIALOK=0
|
||||
}
|
||||
|
||||
function load_sql_file() {
|
||||
if [[ $PG_PARALLEL -eq 0 ]]
|
||||
then
|
||||
tmp_file=/tmp/$(basename $1)_no_parallel
|
||||
${SED} $1 -e 's/PARALLEL \= [A-Z]*/''/g' -e 's/PARALLEL [A-Z]*/''/g' > $tmp_file
|
||||
${CMD} -d ${DATABASE} -f $tmp_file
|
||||
rm $tmp_file
|
||||
else
|
||||
${CMD} -d ${DATABASE} -f $1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function sql() {
|
||||
local ROLE
|
||||
local QUERY
|
||||
@@ -146,6 +132,7 @@ function create_role_and_schema() {
|
||||
sql "GRANT CONNECT ON DATABASE \"${DATABASE}\" TO ${ROLE};"
|
||||
sql "CREATE SCHEMA ${ROLE} AUTHORIZATION ${ROLE};"
|
||||
sql "SELECT cartodb.CDB_Organization_Create_Member('${ROLE}')"
|
||||
sql "ALTER ROLE ${ROLE} SET search_path TO ${ROLE},cartodb,public;"
|
||||
}
|
||||
|
||||
|
||||
@@ -168,34 +155,46 @@ function create_table() {
|
||||
sql ${ROLE} "CREATE TABLE ${ROLE}.${TABLENAME} ( a int );"
|
||||
}
|
||||
|
||||
function truncate_table() {
|
||||
if [[ $# -ne 2 ]]
|
||||
then
|
||||
log_error "truncate_table requires two arguments: role and table_name"
|
||||
exit 1
|
||||
fi
|
||||
local ROLE="$1"
|
||||
local TABLENAME="$2"
|
||||
sql ${ROLE} "TRUNCATE TABLE ${ROLE}.${TABLENAME};"
|
||||
}
|
||||
|
||||
|
||||
function setup() {
|
||||
${CMD} -c "CREATE DATABASE ${DATABASE}"
|
||||
sql "CREATE SCHEMA cartodb;"
|
||||
sql "CREATE EXTENSION plpythonu;"
|
||||
sql "GRANT USAGE ON SCHEMA cartodb TO public;"
|
||||
${CMD} -c "ALTER DATABASE ${DATABASE} SET search_path = public, cartodb;"
|
||||
sql "CREATE EXTENSION cartodb CASCADE;"
|
||||
${CMD} -c "ALTER DATABASE ${DATABASE} SET search_path = public, cartodb;"
|
||||
|
||||
log_info "########################### BOOTSTRAP ###########################"
|
||||
load_sql_file scripts-available/CDB_Organizations.sql
|
||||
load_sql_file scripts-available/CDB_Conf.sql
|
||||
load_sql_file scripts-available/CDB_Groups.sql
|
||||
load_sql_file scripts-available/CDB_Groups_API.sql
|
||||
|
||||
log_info "############################# SETUP #############################"
|
||||
create_role_and_schema cdb_org_admin
|
||||
sql "SELECT cartodb.CDB_Organization_AddAdmin('cdb_org_admin');"
|
||||
create_role_and_schema cdb_testmember_1
|
||||
create_role_and_schema cdb_testmember_2
|
||||
sql "CREATE ROLE publicuser LOGIN;"
|
||||
sql postgres "DO
|
||||
\$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT *
|
||||
FROM pg_catalog.pg_user
|
||||
WHERE usename = 'publicuser') THEN
|
||||
|
||||
CREATE ROLE publicuser LOGIN;
|
||||
END IF;
|
||||
END
|
||||
\$\$;"
|
||||
sql "GRANT CONNECT ON DATABASE \"${DATABASE}\" TO publicuser;"
|
||||
|
||||
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;'
|
||||
|
||||
sql "SELECT cartodb.CDB_Group_CreateGroup('group_a_tmp')"
|
||||
sql "SELECT cartodb.CDB_Group_RenameGroup('group_a_tmp', 'group_a')"
|
||||
@@ -235,7 +234,6 @@ function tear_down() {
|
||||
|
||||
sql 'DROP ROLE cdb_testmember_1;'
|
||||
sql 'DROP ROLE cdb_testmember_2;'
|
||||
sql 'DROP ROLE publicuser;'
|
||||
sql 'DROP ROLE cdb_org_admin;'
|
||||
|
||||
${CMD} -c "DROP DATABASE ${DATABASE}"
|
||||
@@ -251,6 +249,8 @@ function run_tests() {
|
||||
else
|
||||
TESTS=`cat $0 | perl -n -e'/function (test.*)\(\)/ && print "$1\n"'`
|
||||
fi
|
||||
|
||||
setup
|
||||
for t in ${TESTS}
|
||||
do
|
||||
echo "####################################################################"
|
||||
@@ -259,15 +259,15 @@ function run_tests() {
|
||||
echo "#"
|
||||
echo "####################################################################"
|
||||
clear_partial_result
|
||||
setup
|
||||
log_info "############################# TESTS #############################"
|
||||
eval ${t}
|
||||
if [[ ${PARTIALOK} -ne 0 ]]
|
||||
then
|
||||
FAILED_TESTS+=(${t})
|
||||
fi
|
||||
tear_down
|
||||
done
|
||||
tear_down
|
||||
|
||||
if [[ ${OK} -ne 0 ]]
|
||||
then
|
||||
echo
|
||||
@@ -289,9 +289,14 @@ function test_member_1_cannot_grant_read_permission_to_other_schema_than_its_one
|
||||
}
|
||||
|
||||
function test_member_1_grants_read_permission_and_member_2_can_read() {
|
||||
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);'
|
||||
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
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
|
||||
}
|
||||
|
||||
function test_member_2_cannot_add_table_to_member_1_schema_after_table_permission_added() {
|
||||
@@ -300,10 +305,18 @@ function test_member_2_cannot_add_table_to_member_1_schema_after_table_permissio
|
||||
}
|
||||
|
||||
function test_grant_read_permission_between_two_members() {
|
||||
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9);'
|
||||
sql cdb_testmember_2 'INSERT INTO cdb_testmember_2.bar VALUES (5), (6), (7), (8), (9);'
|
||||
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
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
truncate_table cdb_testmember_2 bar
|
||||
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')"
|
||||
}
|
||||
|
||||
function test_member_2_cannot_write_to_member_1_table() {
|
||||
@@ -317,11 +330,15 @@ function test_member_1_cannot_grant_read_write_permission_to_other_schema_than_i
|
||||
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_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
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
|
||||
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
|
||||
}
|
||||
|
||||
function test_member_2_can_write_to_member_1_table_and_sequence_after_write_permission_is_added() {
|
||||
@@ -329,13 +346,17 @@ function test_member_2_can_write_to_member_1_table_and_sequence_after_write_perm
|
||||
|
||||
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_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
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
|
||||
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
|
||||
sql cdb_testmember_1 "ALTER TABLE cdb_testmember_1.foo DROP cartodb_id;"
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
|
||||
}
|
||||
|
||||
function test_member_2_can_write_to_member_1_table_with_non_sequence_cartodb_id_after_write_permission_is_added() {
|
||||
@@ -343,20 +364,28 @@ function test_member_2_can_write_to_member_1_table_with_non_sequence_cartodb_id_
|
||||
|
||||
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_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 5
|
||||
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
|
||||
sql cdb_testmember_1 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
|
||||
sql cdb_testmember_1 "ALTER TABLE cdb_testmember_1.foo DROP cartodb_id;"
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
|
||||
}
|
||||
|
||||
function test_member_1_removes_access_and_member_2_can_no_longer_query_the_table() {
|
||||
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo VALUES (5), (6), (7), (8), (9), (10);'
|
||||
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_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 6
|
||||
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
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
}
|
||||
|
||||
function test_member_1_removes_access_and_member_2_can_no_longer_write_to_the_table() {
|
||||
@@ -364,12 +393,16 @@ function test_member_1_removes_access_and_member_2_can_no_longer_write_to_the_ta
|
||||
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
|
||||
|
||||
# Cleanup
|
||||
truncate_table cdb_testmember_1 foo
|
||||
}
|
||||
|
||||
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 VALUES (1), (2), (3), (4);'
|
||||
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;'
|
||||
|
||||
@@ -378,7 +411,7 @@ function test_giving_permissions_to_two_tables_and_removing_from_first_table_sho
|
||||
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;' should 4
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo_2;' should 5
|
||||
|
||||
# cdb_testmember_1 removes access to foo table
|
||||
@@ -390,51 +423,60 @@ function test_giving_permissions_to_two_tables_and_removing_from_first_table_sho
|
||||
|
||||
|
||||
#### test tear down
|
||||
truncate_table cdb_testmember_1 foo
|
||||
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_1 'INSERT INTO cdb_testmember_1.foo VALUES (1), (2), (3), (4);'
|
||||
|
||||
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
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
|
||||
# Cleanup
|
||||
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Remove_Organization_Access_Permission('cdb_testmember_1', 'foo');"
|
||||
truncate_table cdb_testmember_1 foo
|
||||
}
|
||||
|
||||
function test_user_can_read_when_it_has_permission_after_organization_permission_is_removed() {
|
||||
create_role_and_schema cdb_testmember_3
|
||||
sql cdb_testmember_1 'INSERT INTO cdb_testmember_1.foo VALUES (1), (2), (3), (4);'
|
||||
|
||||
# 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_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
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
|
||||
sql cdb_testmember_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
sql cdb_testmember_3 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
|
||||
# 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_2 'SELECT count(*) FROM cdb_testmember_1.foo;' should 4
|
||||
sql cdb_testmember_3 'SELECT count(*) FROM cdb_testmember_1.foo;' fails
|
||||
|
||||
# Cleanup
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2')"
|
||||
truncate_table cdb_testmember_1 foo
|
||||
drop_role_and_schema cdb_testmember_3
|
||||
}
|
||||
|
||||
function test_cdb_querytables_returns_schema_and_table_name() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file 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_works_with_parentheses() {
|
||||
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() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file 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() {
|
||||
load_sql_file scripts-available/CDB_QueryStatements.sql
|
||||
load_sql_file 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}"
|
||||
}
|
||||
|
||||
@@ -458,10 +500,6 @@ function test_cdb_usertables_should_work_with_orgusers() {
|
||||
# this is required to enable select from other schema
|
||||
sql postgres "GRANT USAGE ON SCHEMA cdb_testmember_1 TO publicuser";
|
||||
|
||||
|
||||
# test CDB_UserTables with publicuser
|
||||
load_sql_file scripts-available/CDB_UserTables.sql
|
||||
|
||||
sql publicuser "SELECT count(*) FROM CDB_UserTables('all')" should 1
|
||||
sql publicuser "SELECT count(*) FROM CDB_UserTables('public')" should 1
|
||||
sql publicuser "SELECT count(*) FROM CDB_UserTables('private')" should 0
|
||||
@@ -477,6 +515,7 @@ function test_cdb_usertables_should_work_with_orgusers() {
|
||||
# test cdb_testmember_2 can select from cdb_testmember_1's public table
|
||||
sql cdb_testmember_2 "SELECT * FROM cdb_testmember_1.test_perms_pub" should 1
|
||||
|
||||
sql postgres 'REVOKE USAGE ON SCHEMA cdb_testmember_1 FROM publicuser;'
|
||||
sql cdb_testmember_1 "DROP TABLE test_perms_pub"
|
||||
sql cdb_testmember_1 "DROP TABLE test_perms_priv"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user