Compare commits
239 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d43faecaf | ||
|
|
acbaf634dc | ||
|
|
c379946c95 | ||
|
|
711c954d1a | ||
|
|
ffb779eb74 | ||
|
|
8db73ae9bd | ||
|
|
208ebb2724 | ||
|
|
951ec51968 | ||
|
|
8a3d506a53 | ||
|
|
c839f74c63 | ||
|
|
8389b39c00 | ||
|
|
4908bacc4b | ||
|
|
38fa3b485c | ||
|
|
2d473cf693 | ||
|
|
4193ff3874 | ||
|
|
68a0752849 | ||
|
|
815b5b429d | ||
|
|
76bdb3657a | ||
|
|
234373df11 | ||
|
|
a486eed2e3 | ||
|
|
795d92da8d | ||
|
|
58e2e7e238 | ||
|
|
25d27263cb | ||
|
|
bbadcc838e | ||
|
|
b1a0904c07 | ||
|
|
399b680b41 | ||
|
|
7c0636c5f9 | ||
|
|
f58f870457 | ||
|
|
a7c8dc04e3 | ||
|
|
90ee56eb35 | ||
|
|
1032737600 | ||
|
|
24639713f1 | ||
|
|
fff7e926c9 | ||
|
|
7d7ecc06f5 | ||
|
|
5992304b47 | ||
|
|
5c52e7564f | ||
|
|
0b7fbdc1cb | ||
|
|
0bfdeae147 | ||
|
|
89b2999a80 | ||
|
|
2080d6d422 | ||
|
|
bc5e23b143 | ||
|
|
64fae71a37 | ||
|
|
594543916d | ||
|
|
aa9286eaba | ||
|
|
ce762f41ac | ||
|
|
529b12af20 | ||
|
|
f98b6fb0a1 | ||
|
|
30cd4cf1f9 | ||
|
|
1356131ec1 | ||
|
|
9731ce38ec | ||
|
|
07892271e5 | ||
|
|
066c574709 | ||
|
|
4786e0a2ae | ||
|
|
358c89b332 | ||
|
|
fa6f9a8a66 | ||
|
|
091aea088e | ||
|
|
7ecdca1b8c | ||
|
|
9ee3125913 | ||
|
|
279eba95b7 | ||
|
|
b462e969a1 | ||
|
|
5d323456ee | ||
|
|
6e130c336e | ||
|
|
457b614d96 | ||
|
|
415d96082e | ||
|
|
5eddf5ce8e | ||
|
|
006c3cc50f | ||
|
|
2b24390a8a | ||
|
|
69f04bb8b0 | ||
|
|
c96bf7c7d5 | ||
|
|
15a8876d06 | ||
|
|
89e991aae9 | ||
|
|
064b26ccd3 | ||
|
|
5bf35bddc1 | ||
|
|
2b69823949 | ||
|
|
1f01ecae30 | ||
|
|
58fd5d4060 | ||
|
|
a2a1ff6ae8 | ||
|
|
326aae4edb | ||
|
|
2a30eb2fd3 | ||
|
|
0b3ad5e569 | ||
|
|
aa302c237d | ||
|
|
9526f0448f | ||
|
|
3399f2b9a5 | ||
|
|
803b3671d0 | ||
|
|
c3fada29a8 | ||
|
|
86e5f6d317 | ||
|
|
f5f59be5b0 | ||
|
|
d99dc394c2 | ||
|
|
8d7860dc7a | ||
|
|
b5427c65c8 | ||
|
|
3122a0479d | ||
|
|
956e56cd37 | ||
|
|
8f1435c049 | ||
|
|
8302f89413 | ||
|
|
e9050178a8 | ||
|
|
3e34ca4654 | ||
|
|
a067cc7da1 | ||
|
|
2c43943df6 | ||
|
|
417cbe7902 | ||
|
|
9a73703954 | ||
|
|
36ac831bd1 | ||
|
|
b19a5fc3dc | ||
|
|
1358964628 | ||
|
|
efe381ad94 | ||
|
|
f7cce21eb7 | ||
|
|
18267477da | ||
|
|
11ad45306f | ||
|
|
75c7ae98e4 | ||
|
|
3c12cf629f | ||
|
|
7b2100b51e | ||
|
|
580ec38ab8 | ||
|
|
897689dd43 | ||
|
|
808fc9fc25 | ||
|
|
65415bb335 | ||
|
|
06ebb27160 | ||
|
|
bd5ae84e90 | ||
|
|
de5a702510 | ||
|
|
6908fb4672 | ||
|
|
a528a250d4 | ||
|
|
ef43623f77 | ||
|
|
09ad550de3 | ||
|
|
1b0f77aa96 | ||
|
|
45f063d469 | ||
|
|
20989e2f28 | ||
|
|
176d69d09e | ||
|
|
9fdbfda60a | ||
|
|
9a3d93976c | ||
|
|
46b45f6dd4 | ||
|
|
fd14750ce5 | ||
|
|
c595e45c11 | ||
|
|
1cf7074fb1 | ||
|
|
f785e71d3b | ||
|
|
14b8cd7d99 | ||
|
|
213adcca16 | ||
|
|
1a571c8a9c | ||
|
|
8f44f5347a | ||
|
|
f96163265b | ||
|
|
1c67214b09 | ||
|
|
16d08ef52b | ||
|
|
15ac9a2cd9 | ||
|
|
ee61d46100 | ||
|
|
49e7094c8a | ||
|
|
fb910be12f | ||
|
|
34c39662ec | ||
|
|
84cac16d1c | ||
|
|
c1fc07d2ac | ||
|
|
5c3c0f5fc9 | ||
|
|
e68d5eca45 | ||
|
|
16a58c479d | ||
|
|
06bb669d4c | ||
|
|
00a3d6e650 | ||
|
|
f0ff197c56 | ||
|
|
c6885c2972 | ||
|
|
57c32332e2 | ||
|
|
3c71eecbae | ||
|
|
6d9424746c | ||
|
|
c0262a05eb | ||
|
|
aff7ae3e2e | ||
|
|
01757a1b6d | ||
|
|
f946bfe4fe | ||
|
|
a098713bfa | ||
|
|
217ff4ffb9 | ||
|
|
a0bb7b1b03 | ||
|
|
4074173c05 | ||
|
|
603b1ceed8 | ||
|
|
0caf6c50dd | ||
|
|
224b2ce395 | ||
|
|
70eeab5748 | ||
|
|
06c05a1d67 | ||
|
|
6567d5441b | ||
|
|
e10d7c3a27 | ||
|
|
b89a752548 | ||
|
|
90fa45b59d | ||
|
|
eb48e26eec | ||
|
|
85e1b92199 | ||
|
|
2dc6ec618f | ||
|
|
3127b9d398 | ||
|
|
91158fafb0 | ||
|
|
16cf70bb4a | ||
|
|
6ea0013343 | ||
|
|
80bd7f8f10 | ||
|
|
bb061981ad | ||
|
|
e289b4725f | ||
|
|
0b8bada553 | ||
|
|
7b48c2765e | ||
|
|
3d0f580fc2 | ||
|
|
d495bd45ba | ||
|
|
f4b51807a1 | ||
|
|
33b4f3718b | ||
|
|
2e186c8565 | ||
|
|
5bc725c8ab | ||
|
|
73906b60da | ||
|
|
03ff0365fd | ||
|
|
76a2cb9132 | ||
|
|
90c16fdb13 | ||
|
|
a80664e72a | ||
|
|
7e2193d3db | ||
|
|
a18cbeb2cd | ||
|
|
0d5f83b3c4 | ||
|
|
7559257d49 | ||
|
|
1198454046 | ||
|
|
32307ceef0 | ||
|
|
54973c01f2 | ||
|
|
eb4564ecee | ||
|
|
abdc39f122 | ||
|
|
d2450ff361 | ||
|
|
a0fe55bd5d | ||
|
|
56fed12392 | ||
|
|
2f26b44142 | ||
|
|
9596e8d9bc | ||
|
|
06036e2fe8 | ||
|
|
e870cf96e0 | ||
|
|
2bce771488 | ||
|
|
cd4ad29e39 | ||
|
|
d59b826d37 | ||
|
|
1c637f8689 | ||
|
|
ecbdb4a430 | ||
|
|
eb84dd04c9 | ||
|
|
276b5cf9ea | ||
|
|
ec34b8ee28 | ||
|
|
0f21db51b6 | ||
|
|
a074f4df5d | ||
|
|
1a12fd3b69 | ||
|
|
78a75cf22d | ||
|
|
1e3c7ace99 | ||
|
|
c210008184 | ||
|
|
0c43fe2731 | ||
|
|
fdbad0f93c | ||
|
|
131aee1503 | ||
|
|
e858ddfa0b | ||
|
|
edf79d9368 | ||
|
|
c26ab0fdd1 | ||
|
|
8dedd2b3f4 | ||
|
|
11834dfdab | ||
|
|
b957635e78 | ||
|
|
a9b9f1ff6c | ||
|
|
d7b560324a | ||
|
|
7b52058265 | ||
|
|
0ecbbd8e71 |
37
.travis.yml
37
.travis.yml
@@ -1,14 +1,41 @@
|
||||
language: c
|
||||
|
||||
addons:
|
||||
postgresql: 9.3
|
||||
postgresql: 9.5
|
||||
|
||||
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
|
||||
#- sudo apt-get install -q postgresql-9.3-postgis-2.1
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -q postgresql-server-dev-9.3
|
||||
- sudo apt-get install -q postgresql-plpython-9.3
|
||||
|
||||
# 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
|
||||
|
||||
script:
|
||||
- make
|
||||
|
||||
@@ -62,3 +62,7 @@ A useful query:
|
||||
```sql
|
||||
SELECT * FROM pg_extension_update_paths('cartodb') WHERE path IS NOT NULL AND source = cdb_version();
|
||||
```
|
||||
|
||||
## Submitting Contributions
|
||||
|
||||
* You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions).
|
||||
|
||||
53
Makefile
53
Makefile
@@ -1,7 +1,7 @@
|
||||
# cartodb/Makefile
|
||||
|
||||
EXTENSION = cartodb
|
||||
EXTVERSION = 0.13.1
|
||||
EXTVERSION = 0.19.2
|
||||
|
||||
SED = sed
|
||||
|
||||
@@ -55,10 +55,33 @@ UPGRADABLE = \
|
||||
0.11.2 \
|
||||
0.11.3 \
|
||||
0.11.4 \
|
||||
0.11.5 \
|
||||
0.12.0 \
|
||||
0.13.0 \
|
||||
0.13.1 \
|
||||
0.11.5 \
|
||||
0.12.0 \
|
||||
0.13.0 \
|
||||
0.13.1 \
|
||||
0.14.0 \
|
||||
0.14.1 \
|
||||
0.14.2 \
|
||||
0.14.3 \
|
||||
0.14.4 \
|
||||
0.15.0 \
|
||||
0.15.1 \
|
||||
0.16.0 \
|
||||
0.16.1 \
|
||||
0.16.2 \
|
||||
0.16.3 \
|
||||
0.16.4 \
|
||||
0.17.0 \
|
||||
0.17.1 \
|
||||
0.18.0 \
|
||||
0.18.1 \
|
||||
0.18.2 \
|
||||
0.18.3 \
|
||||
0.18.4 \
|
||||
0.18.5 \
|
||||
0.19.0 \
|
||||
0.19.1 \
|
||||
0.19.2 \
|
||||
$(EXTVERSION)dev \
|
||||
$(EXTVERSION)next \
|
||||
$(END)
|
||||
@@ -88,11 +111,12 @@ PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
|
||||
$(EXTENSION)--$(EXTVERSION).sql: $(CDBSCRIPTS) cartodb_version.sql Makefile
|
||||
$(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/:DATABASE_USERNAME/cdb_org_admin/g' \
|
||||
-e "s/''public''/''cartodb''/g" >> $@
|
||||
echo "GRANT USAGE ON SCHEMA cartodb TO public;" >> $@
|
||||
cat cartodb_version.sql >> $@
|
||||
|
||||
@@ -111,6 +135,9 @@ $(EXTENSION).control: $(EXTENSION).control.in Makefile
|
||||
cartodb_version.sql: cartodb_version.sql.in Makefile $(GITDIR)/index
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
|
||||
|
||||
# Needed for consistent `echo` results with backslashes
|
||||
SHELL = bash
|
||||
|
||||
legacy_regress: $(REGRESS_OLD) Makefile
|
||||
mkdir -p sql/test/
|
||||
mkdir -p expected/test/
|
||||
@@ -118,14 +145,14 @@ legacy_regress: $(REGRESS_OLD) Makefile
|
||||
for f in $(REGRESS_OLD); do \
|
||||
tn=`basename $${f} .sql`; \
|
||||
of=sql/test/$${tn}.sql; \
|
||||
echo '\\set ECHO none' > $${of}; \
|
||||
echo '\\a' >> $${of}; \
|
||||
echo '\\t' >> $${of}; \
|
||||
echo '\\set QUIET off' >> $${of}; \
|
||||
echo '\set ECHO none' > $${of}; \
|
||||
echo '\a' >> $${of}; \
|
||||
echo '\t' >> $${of}; \
|
||||
echo '\set QUIET off' >> $${of}; \
|
||||
cat $${f} | \
|
||||
$(SED) -e 's/public\./cartodb./g' >> $${of}; \
|
||||
exp=expected/test/$${tn}.out; \
|
||||
echo '\\set ECHO none' > $${exp}; \
|
||||
echo '\set ECHO none' > $${exp}; \
|
||||
cat test/$${tn}_expect >> $${exp}; \
|
||||
done
|
||||
|
||||
@@ -135,7 +162,7 @@ test_organization:
|
||||
test_extension_new:
|
||||
bash test/extension/test.sh
|
||||
|
||||
legacy_tests: legacy_regress
|
||||
legacy_tests: legacy_regress
|
||||
|
||||
installcheck: legacy_tests test_extension_new test_organization
|
||||
|
||||
|
||||
153
NEWS.md
153
NEWS.md
@@ -1,7 +1,158 @@
|
||||
0.13.0 (2016-02-01)
|
||||
0.19.2 (2017-06-30)
|
||||
* Improved functions to generate unique identifiers #305
|
||||
|
||||
0.19.1 (2017-06-05)
|
||||
|
||||
* Fixed a deadlock problem when trying to regenarate overviews #302
|
||||
|
||||
0.19.0 (2017-04-11)
|
||||
|
||||
* Add new function `CDB_EstimateRowCount` #295
|
||||
|
||||
0.18.5 (2016-11-30)
|
||||
|
||||
* Add to new overview creation strategies #290
|
||||
* Fix tests: race condition with publicuser #157
|
||||
* Fix: CDB_Stats divisions by zero #181
|
||||
* Better implementation of `CDB_EqualIntervalBins` #244
|
||||
* New tests for binning functions #249
|
||||
|
||||
0.18.4 (2016-11-04)
|
||||
|
||||
* No functional changes; fixes the migration from previous versions #288
|
||||
|
||||
0.18.3 (2016-11-03)
|
||||
|
||||
* Exclude analysis cache tables from the quota #281
|
||||
|
||||
0.18.2 (2016-10-20)
|
||||
-------------------
|
||||
|
||||
* Fix: cleanup inconsistent position of `username` column in analysis catalog after upgrades
|
||||
[#285](https://github.com/cartodb/cartodb-postgresql/pull/285)
|
||||
|
||||
0.18.1 (2016-10-19)
|
||||
-------------------
|
||||
|
||||
* Increase analysis limit factor to 2 [#284](https://github.com/CartoDB/cartodb-postgresql/pull/284)
|
||||
|
||||
0.18.0 (2016-10-17)
|
||||
-------------------
|
||||
|
||||
* Fix: exclude NULL geometries when creating Overviews #269
|
||||
* Function to check analysis tables limits #279
|
||||
|
||||
0.17.1 (2016-08-16)
|
||||
-------------------
|
||||
|
||||
* Add cache_tables column to cdb_analysis_catalog table #274.
|
||||
|
||||
|
||||
0.17.0 (2016-07-04)
|
||||
-------------------
|
||||
|
||||
* Add export config for cdb_analysis_catalog table #268.
|
||||
* Add some extra fields to cdb_analysis_catalog table. Track user, error_message for failures, and last entity modifying the node #267.
|
||||
* Exclude overviews from user data size #262.
|
||||
|
||||
|
||||
0.16.4 (2016-05-27)
|
||||
-------------------
|
||||
|
||||
* Change CDB_ZoomFromScale() to use a formula and raise
|
||||
maximum overview level from 23 to 29.
|
||||
[#259](https://github.com/CartoDB/cartodb-postgresql/pull/259)
|
||||
|
||||
* Fix bug in overview creating causing it to fail when `x` or
|
||||
`y` columns exist with non-integer type. Prevent also
|
||||
potential integer overflows limiting maximum overview level
|
||||
to 23.
|
||||
[#258](https://github.com/CartoDB/cartodb-postgresql/pull/258)
|
||||
|
||||
|
||||
0.16.3 (2016-05-09)
|
||||
-------------------
|
||||
|
||||
* Fix overview creation problem for organization users
|
||||
with names that require quoting:
|
||||
[#253](https://github.com/CartoDB/cartodb-postgresql/pull/253)
|
||||
|
||||
0.16.2 (2016-04-27)
|
||||
-------------------
|
||||
|
||||
* Use the mode to aggregate category columns in overviews
|
||||
[#246](https://github.com/CartoDB/cartodb-postgresql/pull/246)
|
||||
|
||||
0.16.1 (2016-04-25)
|
||||
-------------------
|
||||
|
||||
* Optimize column information functions performance
|
||||
[#238](https://github.com/CartoDB/cartodb-postgresql/pull/238)
|
||||
|
||||
* Adjust overview points to pixel CDB_EqualIntervalBins
|
||||
[#242](https://github.com/CartoDB/cartodb-postgresql/pull/242)
|
||||
|
||||
* Compute webmercator resolution using full numeric precision
|
||||
[#243](https://github.com/CartoDB/cartodb-postgresql/pull/243)
|
||||
|
||||
|
||||
0.16.0 (2016-04-15)
|
||||
-------------------
|
||||
* Adds table for storing camshaft analysis nodes
|
||||
[#237](https://github.com/CartoDB/cartodb-postgresql/pull/237)
|
||||
|
||||
0.15.1 (2016-04-15)
|
||||
-------------------
|
||||
* Fix problems with org users in overviews functions
|
||||
[#224](https://github.com/CartoDB/cartodb-postgresql/pull/224)
|
||||
* Add `_feature_count` to overviews
|
||||
[#227](https://github.com/CartoDB/cartodb-postgresql/pull/227)
|
||||
* Change point clustering behaviour of overviews
|
||||
[#228](https://github.com/CartoDB/cartodb-postgresql/pull/228)
|
||||
* Change default tolerance of overviews
|
||||
[#230](https://github.com/CartoDB/cartodb-postgresql/pull/230)
|
||||
* Fix problem with aggregated numerical fields in overviews
|
||||
[#233](https://github.com/CartoDB/cartodb-postgresql/pull/233)
|
||||
* Enhance aggregation of text fields in overviews
|
||||
[#234]https://github.com/CartoDB/cartodb-postgresql/pull/234
|
||||
|
||||
0.15.0 (2016-04-05)
|
||||
-------------------
|
||||
* New function CDB_CreateOverviewsWithToleranceInPixels that adds tolerance parameter for overview creation
|
||||
[#221](https://github.com/CartoDB/cartodb-postgresql/pull/221)
|
||||
* New default value for the overviews tolerance in pixels is 2 (used to be 7.5) (also in #221)
|
||||
* The feature density limit used to choose the reference Z level now depends on the tolerance in pixels (also in #221)
|
||||
* Tables that require an explicit schema can now be passed to overview functions
|
||||
[#220](https://github.com/CartoDB/cartodb-postgresql/pull/220)
|
||||
|
||||
0.14.4 (2016-03-29)
|
||||
-------------------
|
||||
* Fix creating overviews for tables with boolean columns
|
||||
[#214](https://github.com/CartoDB/cartodb-postgresql/pull/214)
|
||||
* Fix tests for some systems [#215](https://github.com/CartoDB/cartodb-postgresql/pull/215)
|
||||
|
||||
0.14.3 (2016-03-17)
|
||||
-------------------
|
||||
* Fix for `cartodb_id` bigint casting hardcoded in 0.14.2 to support `cartodb_id` text columns [#210](https://github.com/CartoDB/cartodb-postgresql/pull/210)
|
||||
|
||||
0.14.2 (2016-03-15)
|
||||
-------------------
|
||||
* Support text `cartodb_id` columns in `_CDB_Has_Usable_Primary_ID` [#202](https://github.com/CartoDB/cartodb-postgresql/pull/202)
|
||||
|
||||
0.14.1 (2016-03-07)
|
||||
-------------------
|
||||
* Fully qualify table names in cache cdb_invalidate_varnish calls [#198](https://github.com/CartoDB/cartodb-postgresql/issues/198)
|
||||
|
||||
0.14.0 (2016-02-14)
|
||||
-------------------
|
||||
* Add CDB_ForeignTable.sql to support FDW's [#199](https://github.com/CartoDB/cartodb-postgresql/pull/199)
|
||||
|
||||
0.13.1 (2016-02-01)
|
||||
-------------------
|
||||
* Fix migration fron unpackaged. [193](https://github.com/CartoDB/cartodb-postgresql/pull/193)
|
||||
|
||||
0.13.0 (2016-01-29)
|
||||
-------------------
|
||||
* Add CDB_CreateOverviews, CDB_DropOverviews and CDB_Overviews for vector overviews support. [185](https://github.com/CartoDB/cartodb-postgresql/pull/185)
|
||||
* Convert some simple functions from plpgsql to sql. [188](https://github.com/CartoDB/cartodb-postgresql/pull/188)
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
cartodb-postgresql
|
||||
==================
|
||||
|
||||
[]
|
||||
(http://travis-ci.org/CartoDB/cartodb-postgresql)
|
||||
[](http://travis-ci.org/CartoDB/cartodb-postgresql)
|
||||
|
||||
PostgreSQL extension for CartoDB
|
||||
|
||||
|
||||
25
doc/CDB_EstimateRowCount.md
Normal file
25
doc/CDB_EstimateRowCount.md
Normal file
@@ -0,0 +1,25 @@
|
||||
Estimate the number of rows of a query.
|
||||
|
||||
|
||||
#### Using the function
|
||||
|
||||
```sql
|
||||
SELECT CDB_EstimateRowCount($$
|
||||
UPDATE addresses SET the_geom = cdb_geocode_street_point(addr, city, state, 'US');
|
||||
$$) AS row_count;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
row_count
|
||||
-----------
|
||||
5
|
||||
(1 row)
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
CDB_EstimateRowCount(query)
|
||||
|
||||
* **query** text: the SQL query to estimate the row count for.
|
||||
@@ -2,18 +2,25 @@ Overviews are tables that represent a *reduced* version of a dataset intended
|
||||
for efficient rendering at certain zoom levels while preserving the
|
||||
general visual appearance of the complete dataset.
|
||||
|
||||
The *reduction* consists in a fewer number of records
|
||||
The *reduction* consists in havig a fewer number of records
|
||||
(while each overview record may represent an aggregation of multiple records)
|
||||
and/or simplified record geometries.
|
||||
|
||||
Overviews are created through the `CDB_CreateOverviews`.
|
||||
Overviews are created through the `CDB_CreateOverviews` function.
|
||||
The statement timeout may need to be adjusted before using this function,
|
||||
as overview creation for large tables is a time-consuming operation.
|
||||
|
||||
The `CDB_Overviews` function can be used determine what overview tables
|
||||
exist for a given dataset table and which zoom levels correspond to it.
|
||||
|
||||
The `CDB_DropOverviews` remove a dataset's existing overviews.
|
||||
The `CDB_DropOverviews` function removes a dataset's existing overviews.
|
||||
|
||||
To know if overview tables exist for some base table, and to obtain
|
||||
a list of which overview tables are approrpiate for which zoom levels,
|
||||
the `CDB_Overviews` functions can be used.
|
||||
|
||||
The zoom level we're referring here to are those used
|
||||
by the tiler: http://wiki.openstreetmap.org/wiki/Zoom_levels
|
||||
|
||||
### CDB_CreateOverviews
|
||||
|
||||
@@ -48,6 +55,17 @@ CDB_CreateOverviews(table_name, ref_z_strategy, reduction_strategy)
|
||||
- **base_z** integer, base Z level assigned to the base table.
|
||||
- **overview_z** integer, Z level for which to generate the overview.
|
||||
|
||||
#### Tolerance / level of detail
|
||||
|
||||
The level of detail to be representable by each overview layer can
|
||||
be specified as a tolerance in pixels (if different from the default of 1 pixel)
|
||||
with the function `CDB_CreateOverviewsWithToleranceInPixels`
|
||||
which has as a second additional argument the desired tolerance.
|
||||
|
||||
This tolerance defines the maximum deviation in pixels of the overviews
|
||||
geometries with respect to the original geometries when overview tables
|
||||
are used for their intendend zoom level.
|
||||
|
||||
### CDB_Overviews
|
||||
|
||||
Obtain overview metadata for a given table (existing overviews).
|
||||
@@ -72,7 +90,7 @@ SELECT CDB_Overviews(CDB_QueryTablesText('SELECT * FROM table1, table2'));
|
||||
The result of `CDB_Overviews` has three columns:
|
||||
|
||||
| base_table | z | overview_table |
|
||||
|------------+---+----------------|
|
||||
| ---------- | - | -------------- |
|
||||
| table1 | 1 | table1_ov1 |
|
||||
| table1 | 2 | table1_ov2 |
|
||||
| table1 | 4 | table1_ov4 |
|
||||
|
||||
@@ -3,7 +3,11 @@ List the name of available tables (only the usable ones)
|
||||
#### Using the function
|
||||
|
||||
```sql
|
||||
--- Returns a row for each table having given permission with the table name
|
||||
--- Returns a row for each table having given permission with the table name.
|
||||
--- It also returns tables from others users if you've permission to see them. For example, consider the following scenario:
|
||||
--- User X and User Y at account C.
|
||||
--- User X has a public table T.
|
||||
--- User Y will see table T.
|
||||
--- Currently accepted permissions are: 'public', 'private' or 'all'
|
||||
SELECT CDB_UserTables(perms)
|
||||
```
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
A "cartodb" user table is a table with a well-known set of fields and a well-known set of triggers attached on.
|
||||
|
||||
The fields are:
|
||||
|
||||
- `cartodb_id`, a numerical primary key of serial type
|
||||
- `created_at`, timestamp with timezone not null default now()
|
||||
- `updated_at`, timestamp with timezone not null default now()
|
||||
- `the_geom`, geometry, GiST indexed, constrained (see below)
|
||||
- `the_geom_webmercator`, geometry, GiST indexed, constrained (see below)
|
||||
|
||||
The values of "the_geom" and "the_geom_webmercator" must match these constraints:
|
||||
|
||||
- Only POINT, MULTILINE, MULTIPOLYGON types ? Maybe UNCONSTRAINED
|
||||
- Only 2 dimensions ? Maybe UNCONSTRAINED
|
||||
- SRID=4326 for the_geom and SRID=3857 for the_geom_webmercator
|
||||
|
||||
The triggers are:
|
||||
|
||||
- `track_updates` after modifying statement updates cdb_tablemetadata
|
||||
- `test_quota` before changing statement to forbid if overquota
|
||||
- `test_quota_per_row` before changing row to forbod if overquota (checked on a probabilistic basis)
|
||||
- `update_the_geom_webmercator` before insert or update row to maintain the_geom_webmercator
|
||||
- `update_updated_at_trigger` before update row to maintain updated_at
|
||||
|
||||
Some conversions will be attempted to perform upon cartodbfication when certain fields appear:
|
||||
|
||||
- `cartodb_id`: If found type TEXT will be attempted to cast
|
||||
- `created_at`: If found type TEXT will be attempted to cast
|
||||
- `updated_at`: If found type TEXT will be attempted to cast
|
||||
68
doc/CartoDB-user-table.rst
Normal file
68
doc/CartoDB-user-table.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
CartoDB User Table
|
||||
==================
|
||||
|
||||
Introduction
|
||||
----------
|
||||
A CartoDB user table is a table with a well-known set of columns and a well-known set of triggers attached on.
|
||||
|
||||
Columns
|
||||
----------
|
||||
The required columns of a CartoDB table are:
|
||||
|
||||
- ``cartodb_id``
|
||||
- This column will be used as the primary key of the table and it has a sequence as default value
|
||||
- Its values must be integer, non-zero, non-null and unique
|
||||
- B-Tree indexed
|
||||
- ``the_geom``
|
||||
- This column stores the main geometric features of a table
|
||||
- The type of the column in the Postgres database is ``geometry(Geometry,4326)```
|
||||
- GiST indexed
|
||||
- geometry, GiST indexed, constrained (see below)
|
||||
- ``the_geom_webmercator``
|
||||
- This column stores the geometries used for rendering purposes
|
||||
- The type of the column in the Postgres database is ``geometry(Geometry,3857)``
|
||||
- GiST indexed
|
||||
- This column is automatically updated by the system when the ``the_geom`` column is updated or when there is an insertion of a new row into the table (See triggers below)
|
||||
|
||||
The values of ``the_geom`` and ``the_geom_webmercator`` must be two-dimensional Points, MultiLineStrings or MultiPolygons. Different geometric types in a CartoDB table are not supported.
|
||||
|
||||
Described table example
|
||||
^^^^^^^^^^
|
||||
::
|
||||
|
||||
Column | Type | Modifiers
|
||||
----------------------+-------------------------+--------------------------------------------------------
|
||||
cartodb_id | bigint | not null default nextval('t_cartodb_id_seq'::regclass)
|
||||
the_geom | geometry(Geometry,4326) |
|
||||
the_geom_webmercator | geometry(Geometry,3857) |
|
||||
Indexes:
|
||||
"table_name_pkey" PRIMARY KEY, btree (cartodb_id)
|
||||
"table_name_the_geom_idx" gist (the_geom)
|
||||
"table_name_the_geom_webmercator_idx" gist (the_geom_webmercator)
|
||||
|
||||
Triggers
|
||||
----------
|
||||
The triggers generated in each CartoDB table are:
|
||||
|
||||
- ``track_updates`` after modifying statement updates ``cdb_tablemetadata``
|
||||
- ``test_quota`` before changing statement to forbid if overquota
|
||||
- ``test_quota_per_row`` before insert ot update row to forbid if overquota (checked on a probabilistic basis)
|
||||
- ``update_the_geom_webmercator`` before insert or update row to maintain the ``the_geom_webmercator`` updated with the contents in ``the_geom``
|
||||
|
||||
Described triggers example
|
||||
^^^^^^^^^^
|
||||
::
|
||||
|
||||
test_quota BEFORE INSERT OR UPDATE ON t FOR EACH STATEMENT EXECUTE PROCEDURE cdb_checkquota('0.1', '-1', 'public')
|
||||
test_quota_per_row BEFORE INSERT OR UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE cdb_checkquota('0.001', '-1', 'public')
|
||||
track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON t FOR EACH STATEMENT EXECUTE PROCEDURE cdb_tablemetadata_trigger()
|
||||
update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON t FOR EACH ROW EXECUTE PROCEDURE _cdb_update_the_geom_webmercator()
|
||||
|
||||
|
||||
Further details
|
||||
----------
|
||||
|
||||
Some conversions will be attempted to perform upon cartodbfication when certain fields appear:
|
||||
|
||||
- ``cartodb_id``: If found type TEXT will be attempted to cast to integer. If not casteable, an eror will be raised.
|
||||
- ``the_geom``: If found type TEXT will be attempted to cast to geometry(Geometry,4326).
|
||||
@@ -1,59 +1,63 @@
|
||||
CartoDBfy Requirements
|
||||
======================
|
||||
|
||||
Introduction
|
||||
============
|
||||
------------
|
||||
|
||||
This document aims at describing what cartodbfy is and what its formal requirements are, with the following goals in mind:
|
||||
This document aims at describing what the CartoDBfication is and what its formal requirements are, with the following goals in mind:
|
||||
|
||||
- clarify what are the expectations of the "cartodbfycation process".
|
||||
- define an important part of what should be a stable, public API
|
||||
- allow for better testing, which should in turn...
|
||||
- Clarify what are the expectations of the "cartodbfycation process".
|
||||
- Define an important part of what should be a stable, public API
|
||||
- Allow for better testing, which should in turn...
|
||||
- ...ease modifications and increase quality of the code
|
||||
|
||||
|
||||
What is the CartoDBfycation
|
||||
---------------------------
|
||||
|
||||
What is the cartodbfycation
|
||||
===========================
|
||||
|
||||
The cartodbfycation is the process of converting an arbitrary postgres table into a valid CartoDB table, and register it in the system so that it can be used in the CartoDB editor and platform to generate maps and analysis.
|
||||
|
||||
The CartoDBfycation is the process of converting an arbitrary postgres table into a valid CartoDB table, and register it in the system so that it can be used in the CartoDB editor and platform to generate maps and analysis.
|
||||
|
||||
It is performed by running the function ``CDB_CartodbfyTable(reloid REGCLASS)`` over a target table.
|
||||
|
||||
Valid CartoDB tables
|
||||
====================
|
||||
--------------------
|
||||
|
||||
A valid CartoDB table shall meet the following conditions:
|
||||
|
||||
- Have a ``cartodb_id`` integer column as primary key with a sequence as default value
|
||||
- Have a ``cartodb_id`` column with integer, unique, non-zero and non-null values as primary key with a sequence as default value
|
||||
- Have a ``the_geom`` column of type ``Geometry`` with SRID 4326
|
||||
- Have a ``the_geom_webmercator`` column of type ``Geometry`` with SRID 3857
|
||||
- The columns ``the_geom`` and ``the_geom_webmercator`` shall be in sync
|
||||
- The columns ``the_geom`` and ``the_geom_webmercator`` shall be in sync (task of the ``update_the_geom_webmercator`` trigger)
|
||||
|
||||
Additionally, a CartoDB table can contain other columns.
|
||||
|
||||
See the `CartoDB User Table documentation`_
|
||||
|
||||
.. _CartoDB User Table documentation: https://github.com/CartoDB/cartodb-postgresql/blob/master/doc/CartoDB-user-table.rst
|
||||
for further information.
|
||||
|
||||
High level requirements
|
||||
=======================
|
||||
-----------------------
|
||||
|
||||
Here is a list of high level requirments for the public function ``CDB_CartodbfyTable()``:
|
||||
|
||||
- A call to ``CDB_CartodbfyTable()`` shall modify/rewrite the table and produce a valid CartoDB table with the same name.
|
||||
- A call to ``CDB_CartodbfyTable()`` shall cause the registration of the table into the platform
|
||||
- It shall be idempotent, meaning that successive calls to ``CDB_CartodbfyTable()`` shall not produce any visible effect in the system.
|
||||
- A call to the function shall modify/rewrite the table and produce a valid CartoDB table with the same name.
|
||||
- A call to the function shall cause the registration of the table into the platform.
|
||||
- It shall be idempotent, meaning that successive calls to the function shall not produce any visible effect in the system.
|
||||
- If there's a column containing a geometry, it shall be used to generate ``the_geom`` and the ``the_geom_webmercator`` columns.
|
||||
- Exporting and re-importing the same table in CartoDB shall produce equivalent tables, with the same features associated to the same ``cartodb_id``'s.
|
||||
|
||||
Note that there should be only one feature per row in the source table. If there's more than one, then which one is used for ``the_geom`` and ``the_geom_webmercator`` fields is not determined.
|
||||
|
||||
Note that there should be only one geometry per row in the source table. If there's more than one, then which one is used for ``the_geom`` and ``the_geom_webmercator`` fields is not determined.
|
||||
|
||||
|
||||
Low-level requirements
|
||||
======================
|
||||
----------------------
|
||||
|
||||
- If the original table contains a valid (unique and not null) ``cartodb_id`` column, it shall be used
|
||||
- If the original table contains a ``the_geom`` column or a ``the_geom_webmercator`` column in the expected projection (EPSG 4326 and EPSG 3857, respectively) they shall be used.
|
||||
- If the original table contains a valid (integer, unique, non-zero and not null) ``cartodb_id`` column, it shall be used
|
||||
- If the original table contains a ``the_geom`` column or a ``the_geom_webmercator`` geometric column in the expected projection (EPSG 4326 and EPSG 3857, respectively) they shall be used.
|
||||
- A modification of a cartodbfy'ed table shall insert or update a row in ``CDB_TableMetadata``
|
||||
- A cartodbfy'ed table shall have a ``btree`` index on ``cartodb_id``
|
||||
- A cartodbfy'ed table shall have ``gist`` indices on ``the_geom`` and ``the_geom_webmercator``
|
||||
- Cartodbfy shall deal with text columns for imports, regarding CartoDB columns
|
||||
|
||||
- Cartodbfy shall deal with text columns for imports, regarding CartoDB columns (``cartodb_id``, ``the_geom``, ``the_geom_webmercator``)
|
||||
|
||||
|
||||
95
scripts-available/CDB_AnalysisCatalog.sql
Normal file
95
scripts-available/CDB_AnalysisCatalog.sql
Normal file
@@ -0,0 +1,95 @@
|
||||
-- Table to register analysis nodes from https://github.com/cartodb/camshaft
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
cartodb.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'
|
||||
analysis_def json NOT NULL,
|
||||
-- can reference other nodes in this very same table, allowing recursive queries
|
||||
input_nodes char(40) ARRAY NOT NULL DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
CONSTRAINT valid_status CHECK (
|
||||
status IN ( 'pending', 'waiting', 'running', 'canceled', 'failed', 'ready' )
|
||||
),
|
||||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||||
-- should be updated when some operation was performed in the node
|
||||
-- and anything associated to it might have changed
|
||||
updated_at timestamp with time zone DEFAULT NULL,
|
||||
-- should register last time the node was used
|
||||
used_at timestamp with time zone NOT NULL DEFAULT now(),
|
||||
-- should register the number of times the node was used
|
||||
hits NUMERIC DEFAULT 0,
|
||||
-- should register what was the last node using current node
|
||||
last_used_from char(40),
|
||||
-- last job modifying the node
|
||||
last_modified_by uuid,
|
||||
-- store error message for failures
|
||||
last_error_message text,
|
||||
-- cached tables involved in the analysis
|
||||
cache_tables regclass[] NOT NULL DEFAULT '{}',
|
||||
-- useful for multi account deployments
|
||||
username text
|
||||
);
|
||||
|
||||
-- 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', '');
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Migrations to add new columns from old versions.
|
||||
-- IMPORTANT: Those columns will be added in order of creation. To be consistent
|
||||
-- in column order, ensure that new columns are added at the end and in the same order.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN last_modified_by uuid;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN last_error_message text;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN cache_tables regclass[] NOT NULL DEFAULT '{}';
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
BEGIN
|
||||
ALTER TABLE cartodb.cdb_analysis_catalog ADD COLUMN username text;
|
||||
EXCEPTION
|
||||
WHEN duplicate_column THEN END;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- We want the "username" column to be moved to the last position if it was on a position from other versions
|
||||
-- see https://github.com/CartoDB/cartodb-postgresql/issues/276
|
||||
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;
|
||||
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;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
62
scripts-available/CDB_AnalysisCheck.sql
Normal file
62
scripts-available/CDB_AnalysisCheck.sql
Normal file
@@ -0,0 +1,62 @@
|
||||
-- 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()
|
||||
RETURNS float8 AS
|
||||
$$
|
||||
BEGIN
|
||||
RETURN CDB_Conf_GetConf('analysis_quota_factor')::text::float8;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql' STABLE SECURITY DEFINER;
|
||||
|
||||
|
||||
-- Get the factor (fraction of the quota) for Camshaft cached analysis tables
|
||||
CREATE OR REPLACE FUNCTION _CDB_AnalysisQuotaFactor()
|
||||
RETURNS float8 AS
|
||||
$$
|
||||
DECLARE
|
||||
factor float8;
|
||||
BEGIN
|
||||
-- We use a floating point cdb_conf parameter
|
||||
factor := _CDB_GetConfAnalysisQuotaFactor();
|
||||
-- With a default value
|
||||
IF factor IS NULL THEN
|
||||
factor := 2;
|
||||
END IF;
|
||||
RETURN factor;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql' STABLE;
|
||||
|
||||
-- This checks the space used up by Camshaft cached analysis tables.
|
||||
-- An exception will be raised if the limits are exceeded.
|
||||
-- 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)
|
||||
RETURNS void AS
|
||||
$$
|
||||
DECLARE
|
||||
schema_name TEXT;
|
||||
user_name TEXT;
|
||||
nominal_quota int8;
|
||||
cache_size float8;
|
||||
BEGIN
|
||||
-- We rely on the search_path to determine the user's schema and
|
||||
-- check for all analysis tables in that schema.
|
||||
-- An alternative would be to use cdb_analysis_catalog to
|
||||
-- select analysis tables (cache_tables) from the same user, analysis or node.
|
||||
-- For example:
|
||||
-- SELECT unnest(cache_tables) FROM cdb_analysis_catalog
|
||||
-- WHERE username IN (SELECT username FROM cdb_analysis_catalog
|
||||
-- WHERE table_name::regclass = ANY (cache_tables));
|
||||
-- At the moment we're not using the provided table_name.
|
||||
|
||||
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
|
||||
-- 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;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
55
scripts-available/CDB_AnalysisSupport.sql
Normal file
55
scripts-available/CDB_AnalysisSupport.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- Internal auxiliar functions to deal with [Camshaft](https://github.com/cartodb/camshaft) cached analysis tables.
|
||||
|
||||
-- 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)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN table_name SIMILAR TO '\Aanalysis_[0-9a-f]{10}_[0-9a-f]{40}\Z';
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- This function returns a relation of Camshaft cached analysis tables in the given schema.
|
||||
-- If the schema name parameter is NULL, then tables from all schemas
|
||||
-- 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)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT * FROM _CDB_UserTablesInSchema(schema_name) WHERE _CDB_IsAnalysisTableName(table_name);
|
||||
$$ LANGUAGE 'sql';
|
||||
|
||||
-- This function returns a relation user tables excluding analysis tables
|
||||
-- If the schema name parameter is NULL, then tables from all schemas
|
||||
-- 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)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT * FROM _CDB_UserTablesInSchema(schema_name) WHERE Not _CDB_IsAnalysisTableName(table_name);
|
||||
$$ LANGUAGE 'sql';
|
||||
|
||||
-- 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)
|
||||
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 COALESCE(INT8(SUM(_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;
|
||||
ELSE
|
||||
RETURN 0;
|
||||
END IF;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql' VOLATILE;
|
||||
@@ -470,6 +470,7 @@ $$ LANGUAGE 'plpgsql';
|
||||
-- If the table has both a usable key and usable geometry
|
||||
-- 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)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
@@ -489,114 +490,98 @@ BEGIN
|
||||
-- Do we already have a properly named column?
|
||||
SELECT a.attname, i.indisprimary, i.indisunique, a.attnotnull, a.atttypid
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_type t ON a.atttypid = t.oid
|
||||
LEFT JOIN pg_index i ON c.oid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE c.oid = reloid
|
||||
WHERE c.oid = reloid
|
||||
AND NOT a.attisdropped
|
||||
AND a.attname = const.pkey;
|
||||
|
||||
-- Found something named right...
|
||||
IF FOUND THEN
|
||||
|
||||
-- And it's an integer column...
|
||||
IF rec.atttypid IN (20,21,23) THEN
|
||||
|
||||
-- And it's a unique primary key! Done!
|
||||
IF (rec.indisprimary OR rec.indisunique) AND rec.attnotnull THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('found good ''%s''', const.pkey);
|
||||
RETURN true;
|
||||
|
||||
-- Check and see if the column values are unique and not null,
|
||||
-- if they are, we can use this column...
|
||||
-- And it's a unique primary key! Done!
|
||||
IF (rec.indisprimary OR rec.indisunique) AND rec.attnotnull THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('found good ''%s''', const.pkey);
|
||||
RETURN true;
|
||||
|
||||
-- Check and see if the column values are unique and not null,
|
||||
-- if they are, we can use this column...
|
||||
ELSE
|
||||
|
||||
-- Assume things are OK until proven otherwise...
|
||||
useable_key := true;
|
||||
BEGIN
|
||||
sql := Format('ALTER TABLE %s ADD CONSTRAINT %s_pk PRIMARY KEY (%s)', reloid::text, const.pkey, const.pkey);
|
||||
sql := sql || ', ' || Format('ADD CONSTRAINT %s_integer CHECK (%s::integer >=0);', const.pkey, const.pkey);
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', sql;
|
||||
EXECUTE sql;
|
||||
EXCEPTION
|
||||
-- Failed unique check...
|
||||
WHEN unique_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s is not unique', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed not null check...
|
||||
WHEN not_null_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s contains nulls', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed integer check...
|
||||
WHEN invalid_text_representation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('invalid input syntax for integer %s', const.pkey);
|
||||
useable_key := false;
|
||||
-- Other fatal error
|
||||
WHEN others THEN
|
||||
PERFORM _CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: %s', SQLERRM));
|
||||
END;
|
||||
|
||||
-- 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));
|
||||
|
||||
-- Move non-valid column out of the way
|
||||
ELSE
|
||||
|
||||
-- Assume things are OK until proven otherwise...
|
||||
useable_key := true;
|
||||
|
||||
BEGIN
|
||||
sql := Format('ALTER TABLE %s ADD CONSTRAINT %s_pk PRIMARY KEY (%s)', reloid::text, const.pkey, const.pkey);
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', sql;
|
||||
EXECUTE sql;
|
||||
EXCEPTION
|
||||
-- Failed unique check...
|
||||
WHEN unique_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s is not unique', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed not null check...
|
||||
WHEN not_null_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s contains nulls', const.pkey);
|
||||
useable_key := false;
|
||||
-- Other fatal error
|
||||
WHEN others THEN
|
||||
PERFORM _CDB_Error(sql, '_CDB_Has_Usable_Primary_ID');
|
||||
END;
|
||||
|
||||
-- Clean up test constraint
|
||||
IF useable_key THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_pk', reloid::text, const.pkey));
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-valid ''%s''', const.pkey);
|
||||
|
||||
-- Move non-unique column out of the way
|
||||
ELSE
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-unique ''%s'', renaming it', const.pkey);
|
||||
|
||||
PERFORM _CDB_SQL(
|
||||
Format('ALTER TABLE %s RENAME COLUMN %s TO %I',
|
||||
reloid::text, rec.attname,
|
||||
cartodb._CDB_Unique_Column_Identifier(NULL, const.pkey, NULL, reloid)),
|
||||
'_CDB_Has_Usable_Primary_ID');
|
||||
|
||||
END IF;
|
||||
|
||||
return useable_key;
|
||||
PERFORM _CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, %s', const.pkey));
|
||||
|
||||
END IF;
|
||||
|
||||
-- It's not an integer column, we have to rename it
|
||||
ELSE
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-integer ''%s'', renaming it', const.pkey);
|
||||
|
||||
PERFORM _CDB_SQL(
|
||||
Format('ALTER TABLE %s RENAME COLUMN %s TO %I',
|
||||
reloid::text, rec.attname, cartodb._CDB_Unique_Column_Identifier(NULL, const.pkey, NULL, reloid)),
|
||||
'_CDB_Has_Usable_Primary_ID');
|
||||
|
||||
RETURN useable_key;
|
||||
|
||||
END IF;
|
||||
|
||||
|
||||
-- There's no column there named pkey
|
||||
ELSE
|
||||
|
||||
-- Is there another suitable primary key already?
|
||||
-- Is there another integer suitable primary key already?
|
||||
SELECT a.attname
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_type t ON a.atttypid = t.oid
|
||||
LEFT JOIN pg_index i ON c.oid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE c.oid = reloid AND NOT a.attisdropped
|
||||
AND i.indisprimary AND i.indisunique AND a.attnotnull AND a.atttypid IN (20,21,23);
|
||||
|
||||
|
||||
-- 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');
|
||||
RETURN true;
|
||||
ELSE
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found no useful column for ''%s''', const.pkey);
|
||||
END IF;
|
||||
|
||||
|
||||
END IF;
|
||||
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', 'function complete';
|
||||
|
||||
-- Didn't find re-usable key, so return FALSE
|
||||
RETURN false;
|
||||
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql';
|
||||
|
||||
@@ -825,10 +810,10 @@ DECLARE
|
||||
str TEXT;
|
||||
table_srid INTEGER;
|
||||
geom_srid INTEGER;
|
||||
|
||||
|
||||
has_usable_primary_key BOOLEAN;
|
||||
has_usable_pk_sequence BOOLEAN;
|
||||
|
||||
|
||||
BEGIN
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): %', 'entered function';
|
||||
@@ -839,7 +824,7 @@ BEGIN
|
||||
-- Save the raw schema/table names for later
|
||||
SELECT n.nspname, c.relname, c.relname
|
||||
INTO STRICT relschema, relname, destname
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
|
||||
-- Default the destination to current schema if unspecified
|
||||
@@ -890,7 +875,7 @@ BEGIN
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): has_usable_geoms %', gc.has_usable_geoms;
|
||||
|
||||
-- We can only avoid a rewrite if both the key and
|
||||
-- We can only avoid a rewrite if both the key and
|
||||
-- geometry are usable
|
||||
|
||||
-- No table re-write is required, BUT a rename is required to
|
||||
@@ -934,7 +919,7 @@ BEGIN
|
||||
|
||||
-- Add cartodb ID!
|
||||
IF has_usable_primary_key THEN
|
||||
sql := sql || const.pkey;
|
||||
sql := sql || const.pkey || '::integer ';
|
||||
ELSE
|
||||
sql := sql || 'nextval(''' || destseq || ''') AS ' || const.pkey;
|
||||
END IF;
|
||||
@@ -1096,7 +1081,7 @@ BEGIN
|
||||
|
||||
-- Run it!
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
|
||||
|
||||
-- Set up the primary key sequence
|
||||
-- If we copied the primary key from the original data, we need
|
||||
-- to set the sequence to the maximum value of that key
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
CREATE OR REPLACE FUNCTION CDB_ColumnNames(REGCLASS)
|
||||
RETURNS SETOF information_schema.sql_identifier
|
||||
AS $$
|
||||
|
||||
SELECT c.column_name
|
||||
FROM information_schema.columns c, pg_class _tn, pg_namespace _sn
|
||||
WHERE table_name = _tn.relname
|
||||
AND table_schema = _sn.nspname
|
||||
AND _tn.oid = $1::oid
|
||||
AND _sn.oid = _tn.relnamespace
|
||||
ORDER BY ordinal_position;
|
||||
|
||||
SELECT
|
||||
a.attname::information_schema.sql_identifier column_name
|
||||
FROM pg_class c
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
WHERE c.oid = $1::oid
|
||||
AND a.attstattarget < 0 -- exclude system columns
|
||||
ORDER BY a.attnum;
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
CREATE OR REPLACE FUNCTION CDB_ColumnType(REGCLASS, TEXT)
|
||||
RETURNS information_schema.character_data
|
||||
AS $$
|
||||
|
||||
SELECT c.data_type
|
||||
FROM information_schema.columns c, pg_class _tn, pg_namespace _sn
|
||||
WHERE table_name = _tn.relname
|
||||
AND table_schema = _sn.nspname
|
||||
AND column_name = $2
|
||||
AND _tn.oid = $1::oid
|
||||
AND _sn.oid = _tn.relnamespace;
|
||||
|
||||
SELECT
|
||||
format_type(a.atttypid, NULL)::information_schema.character_data data_type
|
||||
FROM pg_class c
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
WHERE c.oid = $1::oid
|
||||
AND a.attname = $2
|
||||
AND a.attstattarget < 0; -- exclude system columns
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
-- This is to migrate from pre-0.2.0 version
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
--
|
||||
-- Calculate the equal interval bins for a given column
|
||||
--
|
||||
-- @param in_array A numeric array of numbers to determine the best
|
||||
-- to determine the bin boundary
|
||||
-- @param in_array An array of numbers to determine the best
|
||||
-- bin boundary
|
||||
--
|
||||
-- @param breaks The number of bins you want to find.
|
||||
--
|
||||
@@ -11,27 +11,14 @@
|
||||
--
|
||||
--
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_EqualIntervalBins ( in_array NUMERIC[], breaks INT ) RETURNS NUMERIC[] as $$
|
||||
DECLARE
|
||||
diff numeric;
|
||||
min_val numeric;
|
||||
max_val numeric;
|
||||
tmp_val numeric;
|
||||
i INT := 1;
|
||||
reply numeric[];
|
||||
BEGIN
|
||||
SELECT min(e), max(e) INTO min_val, max_val FROM ( SELECT unnest(in_array) e ) x WHERE e IS NOT NULL;
|
||||
diff = (max_val - min_val) / breaks::numeric;
|
||||
LOOP
|
||||
IF i < breaks THEN
|
||||
tmp_val = min_val + i::numeric * diff;
|
||||
reply = array_append(reply, tmp_val);
|
||||
i := i+1;
|
||||
ELSE
|
||||
reply = array_append(reply, max_val);
|
||||
EXIT;
|
||||
END IF;
|
||||
END LOOP;
|
||||
RETURN reply;
|
||||
END;
|
||||
$$ language plpgsql IMMUTABLE;
|
||||
CREATE OR REPLACE FUNCTION 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)
|
||||
SELECT array_agg(bins)
|
||||
FROM (
|
||||
SELECT min + generate_series(1,breaks)*del AS bins
|
||||
FROM stats) q;
|
||||
$$ LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
DROP FUNCTION IF EXISTS CDB_EqualIntervalBins( numeric[], integer);
|
||||
|
||||
31
scripts-available/CDB_EstimateRowCount.sql
Normal file
31
scripts-available/CDB_EstimateRowCount.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
-- Internal function to generate stats for a table if they don't exist
|
||||
CREATE OR REPLACE FUNCTION _CDB_GenerateStats(reloid REGCLASS)
|
||||
RETURNS VOID
|
||||
AS $$
|
||||
DECLARE
|
||||
has_stats BOOLEAN;
|
||||
BEGIN
|
||||
SELECT EXISTS (
|
||||
SELECT * FROM pg_catalog.pg_statistic WHERE starelid = reloid
|
||||
) INTO has_stats;
|
||||
IF NOT has_stats THEN
|
||||
EXECUTE Format('ANALYZE %s;', reloid);
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE 'plpgsql' VOLATILE STRICT SECURITY DEFINER;
|
||||
|
||||
-- Return a row count estimate of the result of a query using statistics
|
||||
CREATE OR REPLACE FUNCTION 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;
|
||||
|
||||
-- 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;
|
||||
199
scripts-available/CDB_ForeignTable.sql
Normal file
199
scripts-available/CDB_ForeignTable.sql
Normal file
@@ -0,0 +1,199 @@
|
||||
---------------------------
|
||||
-- FDW MANAGEMENT FUNCTIONS
|
||||
--
|
||||
-- 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)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
row record;
|
||||
option record;
|
||||
org_role text;
|
||||
BEGIN
|
||||
-- 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;
|
||||
END IF;
|
||||
-- Create FDW first if it does not exist
|
||||
IF NOT EXISTS ( SELECT * FROM pg_foreign_server WHERE srvname = fdw_name)
|
||||
THEN
|
||||
EXECUTE FORMAT('CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw', fdw_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_name) SELECT * from a where options = row.key)
|
||||
THEN
|
||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (ADD %I %L)', fdw_name, row.key, row.value);
|
||||
ELSE
|
||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (SET %I %L)', fdw_name, row.key, row.value);
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
-- Create user mappings
|
||||
FOR row IN SELECT p.key, p.value from lateral json_each(config->'users') p LOOP
|
||||
-- Check if entry on pg_user_mappings exists
|
||||
|
||||
IF NOT EXISTS ( SELECT * FROM pg_user_mappings WHERE srvname = fdw_name AND usename = row.key ) THEN
|
||||
EXECUTE FORMAT ('CREATE USER MAPPING FOR %I SERVER %I', row.key, fdw_name);
|
||||
END IF;
|
||||
|
||||
-- Update user mapping settings
|
||||
FOR option IN SELECT o.key, o.value from lateral json_each_text(row.value) o LOOP
|
||||
IF NOT EXISTS (WITH a AS (select split_part(unnest(umoptions), '=', 1) as options from pg_user_mappings WHERE srvname = fdw_name AND usename = row.key) SELECT * from a where options = option.key) THEN
|
||||
EXECUTE FORMAT('ALTER USER MAPPING FOR %I SERVER %I OPTIONS (ADD %I %L)', row.key, fdw_name, option.key, option.value);
|
||||
ELSE
|
||||
EXECUTE FORMAT('ALTER USER MAPPING FOR %I SERVER %I OPTIONS (SET %I %L)', row.key, fdw_name, option.key, option.value);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
|
||||
-- Create schema if it does not exist.
|
||||
IF NOT EXISTS ( SELECT * from pg_namespace WHERE nspname=fdw_name) THEN
|
||||
EXECUTE FORMAT ('CREATE SCHEMA %I', fdw_name);
|
||||
END IF;
|
||||
|
||||
-- Give the organization role usage permisions over the schema
|
||||
SELECT cartodb.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);
|
||||
END IF;
|
||||
EXECUTE FORMAT ('GRANT SELECT ON %I.cdb_tablemetadata TO %I', fdw_name, org_role);
|
||||
|
||||
END
|
||||
$$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._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;
|
||||
END LOOP;
|
||||
END
|
||||
$$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._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;
|
||||
END
|
||||
$BODY$
|
||||
LANGUAGE plpgsql VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_Add_Remote_Table(source text, table_name text)
|
||||
RETURNS void AS
|
||||
$$
|
||||
BEGIN
|
||||
PERFORM cartodb._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);
|
||||
END
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_Get_Foreign_Updated_At(foreign_table regclass)
|
||||
RETURNS timestamp with time zone AS
|
||||
$$
|
||||
DECLARE
|
||||
remote_table_name text;
|
||||
fdw_schema_name text;
|
||||
time timestamp with time zone;
|
||||
BEGIN
|
||||
-- This will turn a local foreign table (referenced as regclass) to its fully qualified text remote table reference.
|
||||
WITH a AS (SELECT ftoptions FROM pg_foreign_table WHERE ftrelid=foreign_table LIMIT 1),
|
||||
b as (SELECT (pg_options_to_table(ftoptions)).* FROM a)
|
||||
SELECT FORMAT('%I.%I', (SELECT option_value FROM b WHERE option_name='schema_name'), (SELECT option_value FROM b WHERE option_name='table_name'))
|
||||
INTO remote_table_name;
|
||||
|
||||
-- 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;
|
||||
RETURN time;
|
||||
END
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb._cdb_dbname_of_foreign_table(reloid oid)
|
||||
RETURNS TEXT AS $$
|
||||
SELECT option_value FROM pg_options_to_table((
|
||||
|
||||
SELECT fs.srvoptions
|
||||
FROM pg_foreign_table ft
|
||||
LEFT JOIN pg_foreign_server fs ON ft.ftserver = fs.oid
|
||||
WHERE ft.ftrelid = reloid
|
||||
|
||||
)) WHERE option_name='dbname';
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
|
||||
-- Return a set of (dbname, schema_name, table_name, updated_at)
|
||||
-- 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)
|
||||
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
|
||||
), 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)
|
||||
ELSE current_database()
|
||||
END)::text AS dbname,
|
||||
quote_ident(n.nspname::text) schema_name,
|
||||
quote_ident(c.relname::text) table_name,
|
||||
c.relkind,
|
||||
query_tables_oid.reloid
|
||||
FROM query_tables_oid, pg_catalog.pg_class c
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
|
||||
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)
|
||||
END) AS updated_at
|
||||
FROM fqtn;
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
|
||||
-- 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[])
|
||||
RETURNS timestamptz AS $$
|
||||
WITH t AS (
|
||||
SELECT unnest(tables) AS schema_table_name
|
||||
), t_oid 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)
|
||||
END) AS updated_at
|
||||
FROM t_oid
|
||||
LEFT JOIN pg_catalog.pg_class c ON c.oid = reloid
|
||||
) SELECT max(updated_at) FROM t_updated_at;
|
||||
$$ LANGUAGE SQL;
|
||||
@@ -1,3 +1,7 @@
|
||||
-- Create a sequence that belongs to the schema of the extension.
|
||||
-- It will be used to generate unique identifiers within the
|
||||
|
||||
|
||||
-- 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.
|
||||
@@ -15,8 +19,8 @@ DECLARE
|
||||
|
||||
i INTEGER;
|
||||
BEGIN
|
||||
-- Accounts for the _XX incremental suffix in case the identifier is taken
|
||||
usedspace := 3;
|
||||
-- Accounts for the XXXX incremental suffix in case the identifier is taken
|
||||
usedspace := 4;
|
||||
usedspace := usedspace + coalesce(octet_length(prefix), 0);
|
||||
usedspace := usedspace + coalesce(octet_length(suffix), 0);
|
||||
|
||||
@@ -31,7 +35,7 @@ BEGIN
|
||||
i := 0;
|
||||
origident := ident;
|
||||
|
||||
WHILE i < 100 LOOP
|
||||
WHILE i < 10000 LOOP
|
||||
IF schema IS NOT NULL THEN
|
||||
SELECT c.relname, n.nspname
|
||||
INTO rec
|
||||
@@ -51,7 +55,7 @@ BEGIN
|
||||
RETURN ident;
|
||||
END IF;
|
||||
|
||||
ident := origident || '_' || i;
|
||||
ident := origident || i;
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
@@ -76,8 +80,8 @@ DECLARE
|
||||
|
||||
i INTEGER;
|
||||
BEGIN
|
||||
-- Accounts for the _XX incremental suffix in case the identifier is taken
|
||||
usedspace := 3;
|
||||
-- Accounts for the XXXX incremental suffix in case the identifier is taken
|
||||
usedspace := 4;
|
||||
usedspace := usedspace + coalesce(octet_length(prefix), 0);
|
||||
usedspace := usedspace + coalesce(octet_length(suffix), 0);
|
||||
|
||||
@@ -92,7 +96,7 @@ BEGIN
|
||||
i := 0;
|
||||
origident := ident;
|
||||
|
||||
WHILE i < 100 LOOP
|
||||
WHILE i < 10000 LOOP
|
||||
SELECT a.attname
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
@@ -106,7 +110,7 @@ BEGIN
|
||||
RETURN ident;
|
||||
END IF;
|
||||
|
||||
ident := origident || '_' || i;
|
||||
ident := origident || i;
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
|
||||
@@ -1,74 +1,3 @@
|
||||
-- security definer
|
||||
|
||||
-- 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()
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN '\A_vovw_(\d+)_';
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
-- substring(tablename from _CDB_OverviewTableDiscriminator())
|
||||
|
||||
|
||||
-- Pattern matched by the overview tables of a given base table name.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTablePattern(base_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN _CDB_OverviewTableDiscriminator() || base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
-- 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)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN '_vovw_' || z::text || '_' || base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- 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)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN otable SIMILAR TO _CDB_OverviewTablePattern(base_table);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Extract the Z level from an overview table name
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTableZ(otable TEXT)
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN substring(otable from _CDB_OverviewTableDiscriminator())::integer;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Name of the base table corresponding to an overview table
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewBaseTableName(overview_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
IF _CDB_OverviewTableZ(overview_table) IS NULL THEN
|
||||
RETURN overview_table;
|
||||
ELSE
|
||||
RETURN regexp_replace(overview_table, _CDB_OverviewTableDiscriminator(), '');
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
|
||||
-- Remove a dataset's existing overview tables.
|
||||
-- Scope: public
|
||||
-- Parameters:
|
||||
@@ -77,8 +6,11 @@ CREATE OR REPLACE FUNCTION CDB_DropOverviews(reloid REGCLASS)
|
||||
RETURNS void
|
||||
AS $$
|
||||
DECLARE
|
||||
row record;
|
||||
row record;
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
FOR row IN
|
||||
SELECT * FROM CDB_Overviews(reloid)
|
||||
LOOP
|
||||
@@ -100,18 +32,20 @@ $$ LANGUAGE PLPGSQL VOLATILE;
|
||||
CREATE OR REPLACE FUNCTION CDB_Overviews(reloid REGCLASS)
|
||||
RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
||||
AS $$
|
||||
-- FIXME: this will fail if the overview tables
|
||||
-- require a explicit schema name
|
||||
-- possible solutions: return table names as text instead of regclass
|
||||
-- or add schema of reloid before casting to regclass
|
||||
SELECT
|
||||
reloid AS base_table,
|
||||
_CDB_OverviewTableZ(cdb_usertables) AS z,
|
||||
cdb_usertables::regclass AS overview_table
|
||||
FROM CDB_UserTables()
|
||||
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=reloid), cdb_usertables)
|
||||
ORDER BY z;
|
||||
$$ LANGUAGE SQL;
|
||||
DECLARE
|
||||
schema_name TEXT;
|
||||
base_table_name TEXT;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, base_table_name;
|
||||
RETURN QUERY SELECT
|
||||
reloid AS base_table,
|
||||
_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)
|
||||
ORDER BY z;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
-- Return existing overviews (if any) for multiple dataset tables.
|
||||
-- Scope: public
|
||||
@@ -127,30 +61,16 @@ RETURNS TABLE(base_table REGCLASS, z integer, overview_table REGCLASS)
|
||||
AS $$
|
||||
SELECT
|
||||
base_table::regclass AS base_table,
|
||||
_CDB_OverviewTableZ(cdb_usertables) AS z,
|
||||
cdb_usertables::regclass AS overview_table
|
||||
_CDB_OverviewTableZ(table_name) AS z,
|
||||
table_regclass AS overview_table
|
||||
FROM
|
||||
CDB_UserTables(), unnest(tables) base_table
|
||||
WHERE _CDB_IsOverviewTableOf((SELECT relname FROM pg_class WHERE oid=base_table), cdb_usertables)
|
||||
_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)
|
||||
ORDER BY base_table, z;
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
-- Schema and relation names of a table given its reloid
|
||||
-- Scope: private.
|
||||
-- Parameters
|
||||
-- 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)
|
||||
AS $$
|
||||
BEGIN
|
||||
SELECT n.nspname, c.relname
|
||||
INTO STRICT schema_name, table_name
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Calculate the estimated extent of a cartodbfy'ed table.
|
||||
-- Scope: private.
|
||||
-- Parameters
|
||||
@@ -169,17 +89,23 @@ 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$I'', ''%2$I'', ''%3$I'');',
|
||||
'SELECT 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
|
||||
EXCEPTION
|
||||
-- This is the typical ERROR: stats for "mytable" do not exist
|
||||
WHEN internal_error THEN
|
||||
-- Get stats and execute again
|
||||
EXECUTE format('ANALYZE %1$I', reloid);
|
||||
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
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
EXECUTE ext_query INTO ext;
|
||||
END;
|
||||
|
||||
@@ -225,7 +151,11 @@ AS $$
|
||||
WITH RECURSIVE t(x, y, z, e) AS (
|
||||
WITH ext AS (SELECT _cdb_estimated_extent(%6$s) as g),
|
||||
base AS (
|
||||
SELECT (-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)))::integer z
|
||||
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
|
||||
)::integer z
|
||||
FROM ext
|
||||
),
|
||||
lim AS (
|
||||
@@ -247,10 +177,10 @@ 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(x*2 + xx, y*2 + yy, t.z+1)
|
||||
WHERE the_geom_webmercator && 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 < (base.z + %3$s)
|
||||
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;
|
||||
', reloid::text, min_features, nz, n, c, reloid::oid)
|
||||
@@ -266,26 +196,32 @@ $$ LANGUAGE PLPGSQL STABLE;
|
||||
-- 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)
|
||||
CREATE OR REPLACE FUNCTION _CDB_Feature_Density_Ref_Z_Strategy(reloid REGCLASS, tolerance_px FLOAT8 DEFAULT NULL)
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
DECLARE
|
||||
lim FLOAT8 := 500; -- TODO: determine/parameterize this
|
||||
lim FLOAT8;
|
||||
nz integer := 4;
|
||||
fd FLOAT8;
|
||||
c FLOAT8;
|
||||
BEGIN
|
||||
IF (tolerance_px IS NULL) OR tolerance_px = 0 THEN
|
||||
lim := 500;
|
||||
ELSE
|
||||
lim := floor(power(256/tolerance_px, 2))/2;
|
||||
END IF;
|
||||
|
||||
-- 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;
|
||||
-- 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) if the average number of features per tile at level z
|
||||
-- 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 ceil(log(2.0, (c*c*fd/lim)::numeric)/2);
|
||||
RETURN least(_CDB_MaxOverviewLevel()+1, ceil(log(2.0, (c*c*fd/lim)::numeric)/2));
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL STABLE;
|
||||
|
||||
@@ -321,7 +257,7 @@ $$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
-- 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)
|
||||
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)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -330,12 +266,26 @@ AS $$
|
||||
base_name TEXT;
|
||||
class_info RECORD;
|
||||
num_samples INTEGER;
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
overview_rel := _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));
|
||||
|
||||
-- FIXME: handle schema name for overview_rel if reloid requires it
|
||||
EXECUTE Format('DROP TABLE IF EXISTS %I CASCADE;', overview_rel);
|
||||
SELECT * FROM _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
|
||||
RAISE NOTICE 'Sampling reduce stategy deleting and inserting because % has overviews', overview_table_name;
|
||||
EXECUTE Format('DELETE FROM %s;', overview_table_name);
|
||||
creation_clause := Format('INSERT INTO %s', overview_table_name);
|
||||
ELSE
|
||||
RAISE NOTICE 'Sampling reduce stategy creating a new table overview %', overview_table_name;
|
||||
creation_clause := Format('CREATE TABLE %s AS', overview_table_name);
|
||||
END IF;
|
||||
|
||||
-- Estimate number of rows
|
||||
SELECT reltuples, relpages FROM pg_class INTO STRICT class_info
|
||||
@@ -344,21 +294,21 @@ AS $$
|
||||
IF class_info.relpages < 2 OR fraction > 0.5 THEN
|
||||
-- We'll avoid possible CDB_RandomTids problems
|
||||
EXECUTE Format('
|
||||
CREATE TABLE %I AS SELECT * FROM %s WHERE random() < %s;
|
||||
', overview_rel, reloid, fraction);
|
||||
%s SELECT * FROM %s WHERE random() < %s;
|
||||
', creation_clause, reloid, fraction);
|
||||
ELSE
|
||||
num_samples := ceil(class_info.reltuples*fraction);
|
||||
EXECUTE Format('
|
||||
CREATE TABLE %1$I AS SELECT * FROM %2$s
|
||||
%1$s SELECT * FROM %2$s
|
||||
WHERE ctid = ANY (
|
||||
ARRAY[
|
||||
(SELECT CDB_RandomTids(''%2$s'', %3$s))
|
||||
]
|
||||
);
|
||||
', overview_rel, reloid, num_samples);
|
||||
', creation_clause, reloid, num_samples);
|
||||
END IF;
|
||||
|
||||
RETURN overview_rel;
|
||||
RETURN Format('%s', overview_table_name)::regclass;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
@@ -394,9 +344,12 @@ AS $$
|
||||
|
||||
-- preserve the owner of the base table
|
||||
SELECT u.usename
|
||||
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_user u ON (c.relowner=u.usesysid)
|
||||
WHERE c.relname = dataset::text
|
||||
FROM pg_catalog.pg_class c
|
||||
JOIN pg_catalog.pg_user u ON (c.relowner=u.usesysid)
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relname = dataset_name::text AND n.nspname = dataset_scheme
|
||||
INTO table_owner;
|
||||
|
||||
EXECUTE Format('ALTER TABLE IF EXISTS %s OWNER TO %I;', overview_table::text, table_owner);
|
||||
|
||||
-- preserve the table privileges
|
||||
@@ -450,6 +403,70 @@ BEGIN
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL STABLE;
|
||||
|
||||
-- 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)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT a.attname
|
||||
FROM pg_class c
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
LEFT JOIN pg_type t ON t.oid = a.atttypid
|
||||
WHERE c.oid = reloid
|
||||
AND a.attname = col_name
|
||||
AND format_type(a.atttypid, NULL) IN ('text', 'character varying', 'character')
|
||||
AND format_type(a.atttypid, NULL) = format_type(a.atttypid, a.atttypmod)
|
||||
);
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _cdb_categorical_column(reloid REGCLASS, col_name TEXT)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
DECLARE
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
available BOOLEAN;
|
||||
categorical BOOLEAN;
|
||||
BEGIN
|
||||
SELECT * FROM _cdb_split_table_name(reloid) INTO schema_name, table_name;
|
||||
SELECT n_distinct IS NOT NULL
|
||||
FROM pg_stats
|
||||
WHERE pg_stats.schemaname = schema_name
|
||||
AND pg_stats.tablename = table_name
|
||||
AND pg_stats.attname = col_name
|
||||
INTO available;
|
||||
IF available IS NULL OR NOT available THEN
|
||||
EXECUTE Format('ANALYZE %s;', reloid);
|
||||
END IF;
|
||||
SELECT n_distinct > 0 AND n_distinct <= 20
|
||||
FROM pg_stats
|
||||
WHERE pg_stats.schemaname = schema_name
|
||||
AND pg_stats.tablename = table_name
|
||||
AND pg_stats.attname = col_name
|
||||
INTO categorical;
|
||||
RETURN categorical;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _cdb_mode_of_array(anyarray)
|
||||
RETURNS anyelement AS
|
||||
$$
|
||||
SELECT a
|
||||
FROM unnest($1) a
|
||||
GROUP BY 1
|
||||
ORDER BY COUNT(1) DESC, 1
|
||||
LIMIT 1;
|
||||
$$
|
||||
LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
DROP AGGREGATE IF EXISTS _cdb_mode(anyelement);
|
||||
CREATE AGGREGATE _cdb_mode(anyelement) (
|
||||
SFUNC=array_append,
|
||||
STYPE=anyarray,
|
||||
FINALFUNC=_cdb_mode_of_array,
|
||||
INITCOND='{}'
|
||||
);
|
||||
|
||||
-- SQL Aggregation expression for a datase attribute
|
||||
-- Scope: private.
|
||||
-- Parameters
|
||||
@@ -464,6 +481,10 @@ AS $$
|
||||
DECLARE
|
||||
column_type TEXT;
|
||||
qualified_column TEXT;
|
||||
has_counter_column BOOLEAN;
|
||||
feature_count TEXT;
|
||||
total_feature_count TEXT;
|
||||
base_table REGCLASS;
|
||||
BEGIN
|
||||
IF table_alias <> '' THEN
|
||||
qualified_column := Format('%I.%I', table_alias, column_name);
|
||||
@@ -473,19 +494,44 @@ BEGIN
|
||||
|
||||
column_type := CDB_ColumnType(reloid, column_name);
|
||||
|
||||
SELECT EXISTS (
|
||||
SELECT * FROM CDB_ColumnNames(reloid) as colname WHERE colname = '_feature_count'
|
||||
) INTO has_counter_column;
|
||||
IF has_counter_column THEN
|
||||
feature_count := '_feature_count';
|
||||
total_feature_count := 'SUM(_feature_count)';
|
||||
ELSE
|
||||
feature_count := '1';
|
||||
total_feature_count := 'count(*)';
|
||||
END IF;
|
||||
|
||||
base_table := _CDB_OverviewBaseTable(reloid);
|
||||
|
||||
CASE column_type
|
||||
WHEN 'double precision', 'real', 'integer', 'bigint' THEN
|
||||
RETURN Format('AVG(%s)::' || column_type, qualified_column);
|
||||
WHEN 'text' THEN
|
||||
-- TODO: we could define a new aggregate function that returns distinct
|
||||
-- separated values with a limit, adding ellipsis if more values existed
|
||||
-- e.g. with '/' as separator and a limit of three:
|
||||
-- 'A', 'B', 'A', 'C', 'D' => 'A/B/C/...'
|
||||
-- Other ideas: if value is unique then use it, otherwise use something
|
||||
-- like '*' or '(varies)' or '(multiple values)', or NULL
|
||||
-- Using 'string_agg(' || qualified_column || ',''/'')'
|
||||
-- here causes
|
||||
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || 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
|
||||
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
|
||||
RETURN Format('_cdb_mode(%s)::', qualified_column) || column_type;
|
||||
ELSE
|
||||
IF _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;
|
||||
ELSE
|
||||
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
END IF;
|
||||
END IF;
|
||||
WHEN 'boolean' THEN
|
||||
RETURN 'CASE count(*) WHEN 1 THEN BOOL_AND(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
ELSE
|
||||
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
END CASE;
|
||||
@@ -545,33 +591,51 @@ $$ LANGUAGE PLPGSQL STABLE;
|
||||
-- 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)
|
||||
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)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
overview_rel TEXT;
|
||||
reduction FLOAT8;
|
||||
base_name TEXT;
|
||||
grid_px FLOAT8 = 7.5; -- Grid size in pixels at Z level overview_z
|
||||
pixel_m FLOAT8;
|
||||
grid_m FLOAT8;
|
||||
offset_m FLOAT8;
|
||||
offset_x TEXT;
|
||||
offset_y TEXT;
|
||||
cell_x TEXT;
|
||||
cell_y TEXT;
|
||||
aggr_attributes TEXT;
|
||||
attributes TEXT;
|
||||
columns TEXT;
|
||||
gtypes TEXT[];
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
point_geom TEXT;
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _CDB_GeometryTypes(reloid) INTO gtypes;
|
||||
IF array_upper(gtypes, 1) <> 1 OR gtypes[1] <> 'ST_Point' THEN
|
||||
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;
|
||||
RETURN 'x';
|
||||
END IF;
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _CDB_Overview_Name(reloid, ref_z, overview_z);
|
||||
|
||||
-- compute grid cell size using the overview_z dimension...
|
||||
SELECT CDB_XYZ_Resolution(overview_z)*grid_px INTO grid_m;
|
||||
-- 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;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT 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);
|
||||
@@ -582,19 +646,31 @@ AS $$
|
||||
aggr_attributes := aggr_attributes || ', ';
|
||||
END IF;
|
||||
|
||||
-- Center of each cell:
|
||||
cell_x := Format('gx*%1$s + %2$s', grid_m, grid_m/2);
|
||||
cell_y := Format('gy*%1$s + %2$s', grid_m, grid_m/2);
|
||||
|
||||
-- Displacement to the nearest pixel center:
|
||||
IF MOD(grid_px::numeric, 1.0::numeric) = 0 THEN
|
||||
offset_m := pixel_m/2 - MOD((grid_m/2)::numeric, pixel_m::numeric)::float8;
|
||||
offset_x := Format('%s', offset_m);
|
||||
offset_y := Format('%s', offset_m);
|
||||
ELSE
|
||||
offset_x := Format('%2$s/2 - MOD((%1$s)::numeric, (%2$s)::numeric)::float8', cell_x, pixel_m);
|
||||
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);
|
||||
|
||||
-- compute the resulting columns in the same order as in the base table
|
||||
-- cartodb_id,
|
||||
-- ST_Transform(ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857), 4326) AS the_geom,
|
||||
-- ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857) AS the_geom_webmercator
|
||||
-- %4$s
|
||||
WITH cols AS (
|
||||
SELECT
|
||||
CASE c
|
||||
WHEN 'cartodb_id' THEN 'cartodb_id'
|
||||
WHEN 'the_geom' THEN
|
||||
'ST_Transform(ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857), 4326) AS the_geom'
|
||||
Format('ST_Transform(%s, 4326) AS the_geom', point_geom)
|
||||
WHEN 'the_geom_webmercator' THEN
|
||||
'ST_SetSRID(ST_MakePoint(sx/n, sy/n), 3857) AS the_geom_webmercator'
|
||||
Format('%s AS the_geom_webmercator', point_geom)
|
||||
ELSE c
|
||||
END AS column
|
||||
FROM CDB_ColumnNames(reloid) c
|
||||
@@ -603,31 +679,295 @@ AS $$
|
||||
SELECT * FROM cols
|
||||
) AS s INTO columns;
|
||||
|
||||
-- FIXME: handle schema name for overview_rel if reloid requires it
|
||||
EXECUTE Format('DROP TABLE IF EXISTS %I CASCADE;', overview_rel);
|
||||
IF NOT columns LIKE '%_feature_count%' THEN
|
||||
columns := columns || ', n AS _feature_count';
|
||||
END IF;
|
||||
|
||||
overview_table_name := Format('%I.%I', schema_name, overview_rel);
|
||||
IF has_overview_created THEN
|
||||
RAISE NOTICE 'Grid cluster strategy deleting and inserting because % has overviews', overview_table_name;
|
||||
EXECUTE Format('DELETE FROM %s;', overview_table_name);
|
||||
creation_clause := Format('INSERT INTO %s', overview_table_name);
|
||||
ELSE
|
||||
RAISE NOTICE 'Grid cluster strategy creating a new table overview %', overview_table_name;
|
||||
creation_clause := Format('CREATE TABLE %s AS', overview_table_name);
|
||||
END IF;
|
||||
|
||||
-- Now we cluster the data using a grid of size grid_m
|
||||
-- and selecte the centroid (average coordinates) of each cluster.
|
||||
-- If we had a selected numeric attribute of interest we could use it
|
||||
-- as a weight for the average coordinates.
|
||||
EXECUTE Format('
|
||||
CREATE TABLE %3$I AS
|
||||
%3$s
|
||||
WITH clusters AS (
|
||||
SELECT
|
||||
%5$s
|
||||
count(*) AS n,
|
||||
SUM(ST_X(f.the_geom_webmercator)) AS sx,
|
||||
SUM(ST_Y(f.the_geom_webmercator)) AS sy,
|
||||
Floor(ST_X(f.the_geom_webmercator)/%2$s)::int AS gx,
|
||||
Floor(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
|
||||
GROUP BY gx, gy
|
||||
)
|
||||
SELECT %6$s FROM clusters
|
||||
', reloid::text, grid_m, creation_clause, attributes, aggr_attributes, columns);
|
||||
|
||||
RETURN Format('%s', overview_table_name)::regclass;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
-- 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)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
overview_rel TEXT;
|
||||
reduction FLOAT8;
|
||||
base_name TEXT;
|
||||
pixel_m FLOAT8;
|
||||
grid_m FLOAT8;
|
||||
offset_m FLOAT8;
|
||||
offset_x TEXT;
|
||||
offset_y TEXT;
|
||||
cell_x TEXT;
|
||||
cell_y TEXT;
|
||||
aggr_attributes TEXT;
|
||||
attributes TEXT;
|
||||
columns TEXT;
|
||||
gtypes TEXT[];
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
point_geom TEXT;
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _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;
|
||||
END IF;
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _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;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT 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);
|
||||
IF attributes <> '' THEN
|
||||
attributes := ', ' || attributes;
|
||||
END IF;
|
||||
IF aggr_attributes <> '' THEN
|
||||
aggr_attributes := aggr_attributes || ', ';
|
||||
END IF;
|
||||
|
||||
-- Center of each cell:
|
||||
cell_x := Format('gx*%1$s + %2$s', grid_m, grid_m/2);
|
||||
cell_y := Format('gy*%1$s + %2$s', grid_m, grid_m/2);
|
||||
|
||||
-- Displacement to the nearest pixel center:
|
||||
IF MOD(grid_px::numeric, 1.0::numeric) = 0 THEN
|
||||
offset_m := pixel_m/2 - MOD((grid_m/2)::numeric, pixel_m::numeric)::float8;
|
||||
offset_x := Format('%s', offset_m);
|
||||
offset_y := Format('%s', offset_m);
|
||||
ELSE
|
||||
offset_x := Format('%2$s/2 - MOD((%1$s)::numeric, (%2$s)::numeric)::float8', cell_x, pixel_m);
|
||||
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);
|
||||
|
||||
-- compute the resulting columns in the same order as in the base table
|
||||
WITH cols AS (
|
||||
SELECT
|
||||
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'
|
||||
WHEN 'the_geom_webmercator' THEN
|
||||
'ST_SetSRID(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
|
||||
)
|
||||
SELECT string_agg(s.column, ',') FROM (
|
||||
SELECT * FROM cols
|
||||
) AS s INTO columns;
|
||||
|
||||
IF NOT columns LIKE '%_feature_count%' THEN
|
||||
columns := columns || ', n AS _feature_count';
|
||||
END IF;
|
||||
|
||||
overview_table_name := Format('%I.%I', schema_name, overview_rel);
|
||||
IF has_overview_created THEN
|
||||
RAISE NOTICE 'Grid cluster centroid strategy deleting and inserting because % has overviews', overview_table_name;
|
||||
EXECUTE Format('DELETE FROM %s;', overview_table_name);
|
||||
creation_clause := Format('INSERT INTO %s', overview_table_name);
|
||||
ELSE
|
||||
RAISE NOTICE 'Grid cluster centroid strategy creating a new table overview %', overview_table_name;
|
||||
creation_clause := Format('CREATE TABLE %s AS', overview_table_name);
|
||||
END IF;
|
||||
|
||||
-- Now we cluster the data using a grid of size grid_m
|
||||
-- and selecte the centroid (average coordinates) of each cluster.
|
||||
-- If we had a selected numeric attribute of interest we could use it
|
||||
-- as a weight for the average coordinates.
|
||||
EXECUTE Format('
|
||||
%3$s
|
||||
WITH clusters 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,
|
||||
MIN(cartodb_id) AS cartodb_id
|
||||
FROM %1$s f
|
||||
GROUP BY gx, gy
|
||||
)
|
||||
SELECT %6$s FROM clusters
|
||||
', reloid::text, grid_m, overview_rel, attributes, aggr_attributes, columns);
|
||||
', reloid::text, grid_m, creation_clause, attributes, aggr_attributes, columns);
|
||||
|
||||
RETURN overview_rel;
|
||||
RETURN Format('%s', overview_table_name)::regclass;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
-- 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)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
overview_rel TEXT;
|
||||
reduction FLOAT8;
|
||||
base_name TEXT;
|
||||
pixel_m FLOAT8;
|
||||
grid_m FLOAT8;
|
||||
offset_m FLOAT8;
|
||||
offset_x TEXT;
|
||||
offset_y TEXT;
|
||||
cell_x TEXT;
|
||||
cell_y TEXT;
|
||||
aggr_attributes TEXT;
|
||||
attributes TEXT;
|
||||
columns TEXT;
|
||||
gtypes TEXT[];
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
point_geom TEXT;
|
||||
overview_table_name TEXT;
|
||||
creation_clause TEXT;
|
||||
BEGIN
|
||||
SELECT _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;
|
||||
END IF;
|
||||
|
||||
--TODO: check applicability: geometry type, minimum number of points...
|
||||
|
||||
overview_rel := _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;
|
||||
|
||||
-- pixel_m: size of a pixel in webmercator units (meters)
|
||||
SELECT 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);
|
||||
IF attributes <> '' THEN
|
||||
attributes := ', ' || attributes;
|
||||
END IF;
|
||||
IF aggr_attributes <> '' THEN
|
||||
aggr_attributes := aggr_attributes || ', ';
|
||||
END IF;
|
||||
|
||||
-- Center of each cell:
|
||||
cell_x := Format('gx*%1$s + %2$s', grid_m, grid_m/2);
|
||||
cell_y := Format('gy*%1$s + %2$s', grid_m, grid_m/2);
|
||||
|
||||
-- Displacement to the nearest pixel center:
|
||||
IF MOD(grid_px::numeric, 1.0::numeric) = 0 THEN
|
||||
offset_m := pixel_m/2 - MOD((grid_m/2)::numeric, pixel_m::numeric)::float8;
|
||||
offset_x := Format('%s', offset_m);
|
||||
offset_y := Format('%s', offset_m);
|
||||
ELSE
|
||||
offset_x := Format('%2$s/2 - MOD((%1$s)::numeric, (%2$s)::numeric)::float8', cell_x, pixel_m);
|
||||
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);
|
||||
|
||||
-- compute the resulting columns in the same order as in the base table
|
||||
WITH cols AS (
|
||||
SELECT
|
||||
CASE c
|
||||
WHEN 'cartodb_id' THEN 'cartodb_id'
|
||||
ELSE c
|
||||
END AS column
|
||||
FROM CDB_ColumnNames(reloid) c
|
||||
)
|
||||
SELECT string_agg(s.column, ',') FROM (
|
||||
SELECT * FROM cols
|
||||
) AS s INTO columns;
|
||||
|
||||
IF NOT columns LIKE '%_feature_count%' THEN
|
||||
columns := columns || ', n AS _feature_count';
|
||||
END IF;
|
||||
|
||||
overview_table_name := Format('%I.%I', schema_name, overview_rel);
|
||||
IF has_overview_created THEN
|
||||
RAISE NOTICE 'Grid cluster sampling strategy deleting and inserting because % has overviews', overview_table_name;
|
||||
EXECUTE Format('DELETE FROM %s;', overview_table_name);
|
||||
creation_clause := Format('INSERT INTO %s', overview_table_name);
|
||||
ELSE
|
||||
RAISE NOTICE 'Grid cluster sampling strategy creating a new table overview %', overview_table_name;
|
||||
creation_clause := Format('CREATE TABLE %s AS', overview_table_name);
|
||||
END IF;
|
||||
|
||||
-- Now we cluster the data using a grid of size grid_m
|
||||
-- and select the centroid (average coordinates) of each cluster.
|
||||
-- If we had a selected numeric attribute of interest we could use it
|
||||
-- as a weight for the average coordinates.
|
||||
EXECUTE Format('
|
||||
%3$s
|
||||
WITH clusters 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,
|
||||
MIN(cartodb_id) AS cartodb_id
|
||||
FROM %1$s _f
|
||||
GROUP BY gx, gy
|
||||
),
|
||||
cluster_geom AS (
|
||||
SELECT the_geom, the_geom_webmercator, clusters.*
|
||||
FROM clusters INNER JOIN %1$s _g ON (clusters.cartodb_id = _g.cartodb_id)
|
||||
)
|
||||
SELECT %6$s FROM cluster_geom
|
||||
', reloid::text, grid_m, creation_clause, attributes, aggr_attributes, columns);
|
||||
|
||||
RETURN Format('%s', overview_table_name)::regclass;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
@@ -642,7 +982,20 @@ $$ LANGUAGE PLPGSQL;
|
||||
-- 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'::regproc, reduce_strategy regproc DEFAULT '_CDB_GridCluster_Reduce_Strategy'::regproc)
|
||||
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)
|
||||
RETURNS text[]
|
||||
AS $$
|
||||
DECLARE
|
||||
tolerance_px FLOAT8;
|
||||
BEGIN
|
||||
-- Use the default tolerance
|
||||
tolerance_px := 1.0;
|
||||
RETURN CDB_CreateOverviewsWithToleranceInPixels(reloid, tolerance_px, refscale_strategy, reduce_strategy);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
-- 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)
|
||||
RETURNS text[]
|
||||
AS $$
|
||||
DECLARE
|
||||
@@ -653,9 +1006,15 @@ DECLARE
|
||||
overview_z integer;
|
||||
overview_tables REGCLASS[];
|
||||
overviews_step integer := 1;
|
||||
has_counter_column boolean;
|
||||
has_overviews_for_z boolean;
|
||||
BEGIN
|
||||
-- Determine the referece zoom level
|
||||
EXECUTE 'SELECT ' || quote_ident(refscale_strategy::text) || Format('(''%s'');', reloid) INTO ref_z;
|
||||
EXECUTE 'SELECT ' || quote_ident(refscale_strategy::text) || Format('(''%s'', %s);', reloid, tolerance_px) INTO ref_z;
|
||||
|
||||
IF ref_z < 0 OR ref_z IS NULL THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
-- Determine overlay zoom levels
|
||||
-- TODO: should be handled by the refscale_strategy?
|
||||
@@ -665,19 +1024,49 @@ BEGIN
|
||||
overview_z := overview_z - overviews_step;
|
||||
END LOOP;
|
||||
|
||||
-- Create overlay tables
|
||||
-- TODO Check for non-used overview to delete them but we have to be aware that the
|
||||
-- current queries, for example from a tiler, are been used with the old overviews
|
||||
-- so if we remove the overviews in the process this could lead to errors
|
||||
|
||||
-- Create or reganerate overlay tables
|
||||
base_z := ref_z;
|
||||
base_rel := reloid;
|
||||
FOREACH overview_z IN ARRAY overviews_z LOOP
|
||||
EXECUTE 'SELECT ' || quote_ident(reduce_strategy::text) || Format('(''%s'', %s, %s);', base_rel, base_z, overview_z) INTO base_rel;
|
||||
SELECT CASE WHEN count(*) > 0 THEN TRUE ELSE FALSE END from CDB_Overviews(reloid) WHERE z = overview_z INTO has_overviews_for_z;
|
||||
EXECUTE 'SELECT ' || quote_ident(reduce_strategy::text) || Format('(''%s'', %s, %s, %s, ''%s'');', base_rel, base_z, overview_z, tolerance_px, has_overviews_for_z) INTO base_rel;
|
||||
IF base_rel IS NULL THEN
|
||||
EXIT;
|
||||
END IF;
|
||||
base_z := overview_z;
|
||||
PERFORM _CDB_Register_Overview(reloid, base_rel, base_z);
|
||||
IF NOT has_overviews_for_z THEN
|
||||
RAISE NOTICE 'Registering overview: %', base_rel;
|
||||
PERFORM _CDB_Register_Overview(reloid, base_rel, base_z);
|
||||
END IF;
|
||||
SELECT array_append(overview_tables, base_rel) INTO overview_tables;
|
||||
END LOOP;
|
||||
|
||||
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'
|
||||
) INTO has_counter_column;
|
||||
IF NOT has_counter_column THEN
|
||||
EXECUTE Format('
|
||||
ALTER TABLE %s ADD COLUMN _feature_count integer DEFAULT 1;
|
||||
', reloid);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN overview_tables;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
-- 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);
|
||||
|
||||
173
scripts-available/CDB_OverviewsSupport.sql
Normal file
173
scripts-available/CDB_OverviewsSupport.sql
Normal file
@@ -0,0 +1,173 @@
|
||||
-- Auxiliary overviews FUNCTIONS
|
||||
|
||||
-- Maximum zoom level for which overviews may be created
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxOverviewLevel()
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
-- Zoom level will be limited so that both tile coordinates
|
||||
-- and gridding coordinates within a tile up to 1px
|
||||
-- (i.e. tile coordinates / 256)
|
||||
-- can be stored in a 32-bit signed integer.
|
||||
-- We have 31 bits por positive numbers
|
||||
-- For zoom level Z coordinates range from 0 to 2^Z-1, so they
|
||||
-- need Z bits, and need 8 bits more to address pixels within a tile
|
||||
-- (gridding), so we'll limit Z to a maximum of 31 - 8
|
||||
RETURN 23;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Maximum zoom level usable with integer coordinates
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxZoomLevel()
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN 31;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Information about tables in a schema.
|
||||
-- If the schema name parameter is NULL, then tables from all schemas
|
||||
-- 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)
|
||||
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
|
||||
AS $$
|
||||
SELECT
|
||||
c.oid::regclass AS table_regclass,
|
||||
n.nspname::text AS schema_name,
|
||||
c.relname::text AS table_relname
|
||||
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 CASE WHEN schema_name IS NULL
|
||||
THEN n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', 'cartodb')
|
||||
ELSE n.nspname = schema_name
|
||||
END;
|
||||
$$ LANGUAGE 'sql';
|
||||
|
||||
-- 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()
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN '\A_vovw_(\d+)_';
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
-- substring(tablename from _CDB_OverviewTableDiscriminator())
|
||||
|
||||
|
||||
-- Pattern matched by the overview tables of a given base table name.
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTablePattern(base_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN _CDB_OverviewTableDiscriminator() || base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
-- 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)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN '_vovw_' || z::text || '_' || base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- 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)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN otable SIMILAR TO _CDB_OverviewTablePattern(base_table);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Extract the Z level from an overview table name
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewTableZ(otable TEXT)
|
||||
RETURNS INTEGER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN substring(otable from _CDB_OverviewTableDiscriminator())::integer;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Name of the base table corresponding to an overview table
|
||||
-- Scope: private.
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewBaseTableName(overview_table TEXT)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
BEGIN
|
||||
IF _CDB_OverviewTableZ(overview_table) IS NULL THEN
|
||||
RETURN overview_table;
|
||||
ELSE
|
||||
RETURN regexp_replace(overview_table, _CDB_OverviewTableDiscriminator(), '');
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _CDB_OverviewBaseTable(overview_table REGCLASS)
|
||||
RETURNS REGCLASS
|
||||
AS $$
|
||||
DECLARE
|
||||
table_name TEXT;
|
||||
schema_name TEXT;
|
||||
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);
|
||||
IF base_name != table_name THEN
|
||||
base_table := Format('%I.%I', schema_name, base_name)::regclass;
|
||||
ELSE
|
||||
base_table := overview_table;
|
||||
END IF;
|
||||
RETURN base_table;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Schema and relation names of a table given its reloid
|
||||
-- Scope: private.
|
||||
-- Parameters
|
||||
-- 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)
|
||||
AS $$
|
||||
BEGIN
|
||||
SELECT n.nspname, c.relname
|
||||
INTO STRICT schema_name, table_name
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
|
||||
-- Schema and relation names of a table given its reloid
|
||||
-- Scope: private.
|
||||
-- Parameters
|
||||
-- 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)
|
||||
RETURNS TEXT
|
||||
AS $$
|
||||
DECLARE
|
||||
schema_name TEXT;
|
||||
BEGIN
|
||||
SELECT n.nspname
|
||||
INTO STRICT schema_name
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
RETURN schema_name;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL IMMUTABLE;
|
||||
@@ -26,16 +26,15 @@ BEGIN
|
||||
WHERE o_table_schema = schema_name AND o_table_catalog = current_database()
|
||||
),
|
||||
user_tables AS (
|
||||
SELECT table_name FROM information_schema.tables
|
||||
WHERE table_catalog = current_database() AND table_schema = schema_name
|
||||
AND table_name != 'spatial_ref_sys'
|
||||
AND table_name != 'cdb_tablemetadata'
|
||||
AND table_type = 'BASE TABLE'
|
||||
SELECT table_name FROM _CDB_NonAnalysisTablesInSchema(schema_name)
|
||||
),
|
||||
table_cat AS (
|
||||
SELECT
|
||||
table_name,
|
||||
EXISTS(select * from raster_tables where o_table_name = table_name) AS is_overview,
|
||||
(
|
||||
EXISTS(select * from raster_tables where o_table_name = table_name)
|
||||
OR table_name SIMILAR TO _CDB_OverviewTableDiscriminator() || '[\w\d]*'
|
||||
) AS is_overview,
|
||||
EXISTS(SELECT * FROM raster_tables WHERE r_table_name = table_name) AS is_raster
|
||||
FROM user_tables
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-- @param in_array A numeric array of numbers
|
||||
--
|
||||
-- Returns: statistical quantity chosen
|
||||
--
|
||||
--
|
||||
-- References: http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
|
||||
--
|
||||
|
||||
@@ -13,17 +13,21 @@ CREATE OR REPLACE FUNCTION CDB_Kurtosis ( in_array NUMERIC[] ) RETURNS NUMERIC a
|
||||
DECLARE
|
||||
a numeric;
|
||||
c numeric;
|
||||
s numeric;
|
||||
k numeric;
|
||||
BEGIN
|
||||
SELECT AVG(e), COUNT(e)::numeric, stddev(e) INTO a, c, s FROM ( SELECT unnest(in_array) e ) x;
|
||||
SELECT AVG(e), COUNT(e)::numeric * power(stddev(e),4) INTO a, c FROM ( SELECT unnest(in_array) e ) x;
|
||||
|
||||
EXECUTE 'SELECT sum(power($1 - e, 4)) / ( $2 * power($3, 4)) - 3
|
||||
FROM (SELECT unnest($4) e ) x'
|
||||
INTO k
|
||||
USING a, c, s, in_array;
|
||||
IF c=0 THEN
|
||||
RETURN 0;
|
||||
ELSE
|
||||
|
||||
RETURN k;
|
||||
EXECUTE 'SELECT sum(power($1 - e, 4)) / ($2 ) - 3
|
||||
FROM (SELECT unnest($3) e ) x'
|
||||
INTO k
|
||||
USING a, c, in_array;
|
||||
|
||||
RETURN k;
|
||||
END IF;
|
||||
END;
|
||||
$$ language plpgsql IMMUTABLE;
|
||||
|
||||
@@ -32,16 +36,18 @@ CREATE OR REPLACE FUNCTION CDB_Skewness ( in_array NUMERIC[] ) RETURNS NUMERIC a
|
||||
DECLARE
|
||||
a numeric;
|
||||
c numeric;
|
||||
s numeric;
|
||||
sk numeric;
|
||||
BEGIN
|
||||
SELECT AVG(e), COUNT(e)::numeric, stddev(e) INTO a, c, s FROM ( SELECT unnest(in_array) e ) x;
|
||||
SELECT AVG(e), COUNT(e)::numeric * power(stddev(e),3) INTO a, c FROM ( SELECT unnest(in_array) e ) x;
|
||||
IF c=0 THEN
|
||||
RETURN 0;
|
||||
ELSE
|
||||
EXECUTE 'SELECT sum(power($1 - e, 3)) / ( $2 )
|
||||
FROM (SELECT unnest($3) e ) x'
|
||||
INTO sk
|
||||
USING a, c, in_array;
|
||||
|
||||
EXECUTE 'SELECT sum(power($1 - e, 3)) / ( $2 * power($3, 3))
|
||||
FROM (SELECT unnest($4) e ) x'
|
||||
INTO sk
|
||||
USING a, c, s, in_array;
|
||||
|
||||
RETURN sk;
|
||||
RETURN sk;
|
||||
END IF;
|
||||
END;
|
||||
$$ language plpgsql IMMUTABLE;
|
||||
|
||||
@@ -5,6 +5,11 @@ CREATE TABLE IF NOT EXISTS
|
||||
updated_at timestamp with time zone not null default now()
|
||||
);
|
||||
|
||||
CREATE OR REPLACE VIEW public.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
|
||||
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;
|
||||
@@ -61,9 +66,11 @@ CREATE OR REPLACE FUNCTION _CDB_TableMetadata_Updated()
|
||||
RETURNS trigger AS
|
||||
$$
|
||||
DECLARE
|
||||
tabname TEXT;
|
||||
tabname regclass;
|
||||
rec RECORD;
|
||||
found BOOL;
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
BEGIN
|
||||
|
||||
IF TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
|
||||
@@ -98,9 +105,10 @@ BEGIN
|
||||
AND u.usesuper
|
||||
ORDER BY n.nspname
|
||||
LOOP
|
||||
SELECT n.nspname, c.relname FROM pg_class c, pg_namespace n WHERE c.oid=tabname AND c.relnamespace = n.oid INTO schema_name, table_name;
|
||||
EXECUTE 'SELECT ' || quote_ident(rec.nspname) || '.'
|
||||
|| quote_ident(rec.proname)
|
||||
|| '(' || quote_literal(tabname) || ')';
|
||||
|| '(' || quote_literal(quote_ident(schema_name) || '.' || quote_ident(table_name)) || ')';
|
||||
found := true;
|
||||
EXIT;
|
||||
END LOOP;
|
||||
@@ -111,11 +119,11 @@ END;
|
||||
$$
|
||||
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
|
||||
|
||||
DROP TRIGGER IF EXISTS table_modified ON CDB_TableMetadata;
|
||||
DROP TRIGGER IF EXISTS table_modified ON public.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 CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
ON public.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
_CDB_TableMetadata_Updated();
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ SELECT c.relname
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relkind = 'r'
|
||||
AND c.relname NOT IN ('cdb_tablemetadata', 'spatial_ref_sys')
|
||||
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 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')
|
||||
|
||||
@@ -6,7 +6,7 @@ CREATE OR REPLACE FUNCTION CDB_XYZ_Resolution(z INTEGER)
|
||||
RETURNS FLOAT8
|
||||
AS $$
|
||||
-- circumference divided by 256 is z0 resolution, then divide by 2^z
|
||||
SELECT 40075017.0 / 256 / power(2, z);
|
||||
SELECT 6378137.0*2.0*pi() / 256.0 / power(2.0, z);
|
||||
$$ LANGUAGE SQL IMMUTABLE STRICT;
|
||||
-- }
|
||||
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_ZoomFromScale(scaleDenominator numeric) RETURNS int AS $$
|
||||
BEGIN
|
||||
CASE
|
||||
WHEN scaleDenominator > 500000000 THEN RETURN 0;
|
||||
WHEN scaleDenominator <= 500000000 AND scaleDenominator > 200000000 THEN RETURN 1;
|
||||
WHEN scaleDenominator <= 200000000 AND scaleDenominator > 100000000 THEN RETURN 2;
|
||||
WHEN scaleDenominator <= 100000000 AND scaleDenominator > 50000000 THEN RETURN 3;
|
||||
WHEN scaleDenominator <= 50000000 AND scaleDenominator > 25000000 THEN RETURN 4;
|
||||
WHEN scaleDenominator <= 25000000 AND scaleDenominator > 12500000 THEN RETURN 5;
|
||||
WHEN scaleDenominator <= 12500000 AND scaleDenominator > 6500000 THEN RETURN 6;
|
||||
WHEN scaleDenominator <= 6500000 AND scaleDenominator > 3000000 THEN RETURN 7;
|
||||
WHEN scaleDenominator <= 3000000 AND scaleDenominator > 1500000 THEN RETURN 8;
|
||||
WHEN scaleDenominator <= 1500000 AND scaleDenominator > 750000 THEN RETURN 9;
|
||||
WHEN scaleDenominator <= 750000 AND scaleDenominator > 400000 THEN RETURN 10;
|
||||
WHEN scaleDenominator <= 400000 AND scaleDenominator > 200000 THEN RETURN 11;
|
||||
WHEN scaleDenominator <= 200000 AND scaleDenominator > 100000 THEN RETURN 12;
|
||||
WHEN scaleDenominator <= 100000 AND scaleDenominator > 50000 THEN RETURN 13;
|
||||
WHEN scaleDenominator <= 50000 AND scaleDenominator > 25000 THEN RETURN 14;
|
||||
WHEN scaleDenominator <= 25000 AND scaleDenominator > 12500 THEN RETURN 15;
|
||||
WHEN scaleDenominator <= 12500 AND scaleDenominator > 5000 THEN RETURN 16;
|
||||
WHEN scaleDenominator <= 5000 AND scaleDenominator > 2500 THEN RETURN 17;
|
||||
WHEN scaleDenominator <= 2500 AND scaleDenominator > 1500 THEN RETURN 18;
|
||||
WHEN scaleDenominator <= 1500 AND scaleDenominator > 750 THEN RETURN 19;
|
||||
WHEN scaleDenominator <= 750 AND scaleDenominator > 500 THEN RETURN 20;
|
||||
WHEN scaleDenominator <= 500 AND scaleDenominator > 250 THEN RETURN 21;
|
||||
WHEN scaleDenominator <= 250 AND scaleDenominator > 100 THEN RETURN 22;
|
||||
WHEN scaleDenominator <= 100 THEN RETURN 23;
|
||||
END CASE;
|
||||
END
|
||||
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||
-- Maximum supported zoom level
|
||||
CREATE OR REPLACE FUNCTION _CDB_MaxSupportedZoom()
|
||||
RETURNS int
|
||||
LANGUAGE SQL
|
||||
IMMUTABLE
|
||||
AS $$
|
||||
-- The maximum zoom level has to be limited for various reasons,
|
||||
-- e.g. zoom levels greater than 31 would require tile coordinates
|
||||
-- that would not fit in an INTEGER (which is signed, 32 bits long).
|
||||
-- We'll choose 20 as a limit which is safe also when the JavaScript shift
|
||||
-- operator (<<) is used for computing powers of two.
|
||||
SELECT 29;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cartodb.CDB_ZoomFromScale(scaleDenominator numeric)
|
||||
RETURNS int
|
||||
LANGUAGE SQL
|
||||
IMMUTABLE
|
||||
AS $$
|
||||
SELECT
|
||||
CASE
|
||||
WHEN scaleDenominator > 600000000 THEN
|
||||
-- Scale is smaller than zoom level 0
|
||||
NULL
|
||||
WHEN scaleDenominator = 0 THEN
|
||||
-- Actual zoom level would be infinite
|
||||
_CDB_MaxSupportedZoom()
|
||||
ELSE
|
||||
CAST (
|
||||
LEAST(
|
||||
ROUND(LOG(2, 559082264.028/scaleDenominator)),
|
||||
_CDB_MaxSupportedZoom()
|
||||
)
|
||||
AS INTEGER)
|
||||
END;
|
||||
$$;
|
||||
|
||||
1
scripts-enabled/085-CDB_OverviewsSupport.sql
Symbolic link
1
scripts-enabled/085-CDB_OverviewsSupport.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_OverviewsSupport.sql
|
||||
1
scripts-enabled/250-CDB_ForeignTable.sql
Symbolic link
1
scripts-enabled/250-CDB_ForeignTable.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_ForeignTable.sql
|
||||
1
scripts-enabled/260-CDB_AnalysisCatalog.sql
Symbolic link
1
scripts-enabled/260-CDB_AnalysisCatalog.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_AnalysisCatalog.sql
|
||||
1
scripts-enabled/270-CDB_AnalysisSupport.sql
Symbolic link
1
scripts-enabled/270-CDB_AnalysisSupport.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_AnalysisSupport.sql
|
||||
1
scripts-enabled/275-CDB_AnalysisCheck.sql
Symbolic link
1
scripts-enabled/275-CDB_AnalysisCheck.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_AnalysisCheck.sql
|
||||
1
scripts-enabled/280-CDB_EstimateRowCount.sql
Symbolic link
1
scripts-enabled/280-CDB_EstimateRowCount.sql
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts-available/CDB_EstimateRowCount.sql
|
||||
20
test/CDB_AnalysisCheckTest.sql
Normal file
20
test/CDB_AnalysisCheckTest.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
SET client_min_messages TO error;
|
||||
\set VERBOSITY terse
|
||||
|
||||
SELECT CDB_SetUserQuotaInBytes(1000000);
|
||||
SELECT _CDB_AnalysisTablesInSchema('public');
|
||||
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_AnalysisDataSize('public');
|
||||
SELECT CDB_CheckAnalysisQuota('analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94');
|
||||
SELECT CDB_SetUserQuotaInBytes(1);
|
||||
SELECT CDB_CheckAnalysisQuota('analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94');
|
||||
INSERT INTO analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d5(id) VALUES (1),(2),(3),(4),(5);
|
||||
SELECT CDB_CheckAnalysisQuota('analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94');
|
||||
DROP TABLE analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d5;
|
||||
DROP TABLE analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94;
|
||||
DROP TABLE analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da9;
|
||||
DROP FUNCTION "public"._CDB_UserQuotaInBytes();
|
||||
18
test/CDB_AnalysisCheckTest_expect
Normal file
18
test/CDB_AnalysisCheckTest_expect
Normal file
@@ -0,0 +1,18 @@
|
||||
SET
|
||||
1000000
|
||||
0
|
||||
CREATE TABLE
|
||||
CREATE TABLE
|
||||
CREATE TABLE
|
||||
(analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d5,public,analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d5)
|
||||
(analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94,public,analysis_2f13a3dbd7_f00cee44e9e6152b450bde3a92eb9ae0d099da94)
|
||||
0
|
||||
|
||||
1
|
||||
|
||||
INSERT 0 5
|
||||
ERROR: Analysis cache space limits exceeded
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP FUNCTION
|
||||
@@ -1,5 +1,5 @@
|
||||
SET client_min_messages TO error;
|
||||
\set VERBOSITY default
|
||||
\set VERBOSITY terse
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_CartodbfyTableCheck(tabname regclass, label text)
|
||||
RETURNS text AS
|
||||
@@ -124,13 +124,21 @@ END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql';
|
||||
|
||||
-- table with single non-geometrical column
|
||||
-- check cartodbfytable idempotence
|
||||
CREATE TABLE t AS SELECT 1::int as a;
|
||||
SELECT CDB_CartodbfyTable('public', 't'); -- should fail
|
||||
SELECT CDB_SetUserQuotaInBytes(0); -- Set user quota to infinite
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'single non-geometrical column');
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with single non-geometrical column
|
||||
CREATE TABLE t AS SELECT ST_SetSRID(ST_MakePoint(-1,-1),4326) as the_geom, 1::int as cartodb_id, 'this is a sentence' as description;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'check function idempotence');
|
||||
SELECT * FROM t;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'check function idempotence');
|
||||
SELECT * FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with existing srid-unconstrained (but type-constrained) the_geom
|
||||
CREATE TABLE t AS SELECT ST_SetSRID(ST_MakePoint(0,0),4326)::geometry(point) as the_geom;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'srid-unconstrained the_geom');
|
||||
@@ -164,19 +172,22 @@ SELECT CDB_CartodbfyTableCheck('t', 'trigger-protected the_geom');
|
||||
SELECT 'extent',ST_Extent(ST_SnapToGrid(the_geom,0.2)) FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- INFO: disabled because cartodbfy does not longer consider text columns for primary ID
|
||||
-- -- table with existing cartodb_id field of type text
|
||||
-- CREATE TABLE t AS SELECT 10::text as cartodb_id;
|
||||
-- SELECT CDB_CartodbfyTableCheck('t', 'text cartodb_id');
|
||||
-- select cartodb_id/2 FROM t;
|
||||
-- DROP TABLE t;
|
||||
-- table with existing cartodb_id field of type text
|
||||
CREATE TABLE t AS SELECT 10::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'text cartodb_id');
|
||||
select cartodb_id/2 FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- INFO: disabled because cartodbfy does not longer consider text columns for primary ID
|
||||
-- -- table with existing cartodb_id field of type text not casting
|
||||
-- CREATE TABLE t AS SELECT 'nan' as cartodb_id;
|
||||
-- SELECT CDB_CartodbfyTableCheck('t', 'uncasting text cartodb_id');
|
||||
-- select cartodb_id,_cartodb_id0 FROM t;
|
||||
-- DROP TABLE t;
|
||||
-- table with existing cartodb_id field of type text not casting
|
||||
CREATE TABLE t AS SELECT 'nan'::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'uncasting text cartodb_id');
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with empty cartodb_id field of type text
|
||||
CREATE TABLE t AS SELECT null::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'empty text cartodb_id');
|
||||
SELECT cartodb_id from t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with existing cartodb_id field of type int4 not sequenced
|
||||
CREATE TABLE t AS SELECT 1::int4 as cartodb_id;
|
||||
@@ -291,7 +302,7 @@ INSERT INTO test VALUES
|
||||
(NULL),
|
||||
(3);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with null cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
-- Table with non unique cartodb_id
|
||||
@@ -303,7 +314,7 @@ INSERT INTO test VALUES
|
||||
(2),
|
||||
(2);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with non unique cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
-- Table with non unique and null cartodb_id
|
||||
@@ -316,7 +327,7 @@ INSERT INTO test VALUES
|
||||
(NULL),
|
||||
(2);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with non unique and null cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
CREATE TABLE test (
|
||||
@@ -361,6 +372,19 @@ SELECT column_name FROM information_schema.columns WHERE table_name = 'test' AND
|
||||
DROP TABLE test;
|
||||
SET client_min_messages TO error;
|
||||
|
||||
-- Unique identifier generation can break CDB_CartodbfyTable #305
|
||||
BEGIN;
|
||||
DO $$
|
||||
BEGIN
|
||||
FOR i IN 1..150 LOOP
|
||||
EXECUTE 'CREATE TABLE untitled_table();';
|
||||
EXECUTE $query$SELECT CDB_CartodbfyTable('untitled_table');$query$;
|
||||
EXECUTE 'ALTER TABLE untitled_table RENAME TO my_renamed_table_' || i;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
ROLLBACK;
|
||||
|
||||
-- TODO: table with existing custom-triggered the_geom
|
||||
|
||||
DROP FUNCTION CDB_CartodbfyTableCheck(regclass, text);
|
||||
|
||||
@@ -2,12 +2,16 @@ SET
|
||||
CREATE FUNCTION
|
||||
SELECT 1
|
||||
ERROR: Please set user quota before cartodbfying tables.
|
||||
CONTEXT: SQL statement "SELECT cartodb._CDB_check_prerequisites(destschema, reloid)"
|
||||
PL/pgSQL function cdb_cartodbfytable(text,regclass) line 21 at PERFORM
|
||||
0
|
||||
single non-geometrical column cartodbfied fine
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
check function idempotence cartodbfied fine
|
||||
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
|
||||
check function idempotence cartodbfied fine
|
||||
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
srid-unconstrained the_geom cartodbfied fine
|
||||
DROP TABLE
|
||||
SELECT 2
|
||||
@@ -26,6 +30,17 @@ SELECT 1
|
||||
CREATE TRIGGER
|
||||
trigger-protected the_geom cartodbfied fine
|
||||
extent|BOX(1 1,2 2)
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
text cartodb_id cartodbfied fine
|
||||
5
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE t ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE t ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
unsequenced cartodb_id cartodbfied fine
|
||||
@@ -77,30 +92,30 @@ Table with both the_geom and wkb_geometry #141 cartodbfied fine
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
Many colliding columns #141 cartodbfied fine
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: multiple primary keys for table "many_colliding_columns" are not allowed): ALTER TABLE many_colliding_columns ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 4
|
||||
Table with null cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|
|
||||
4|3
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 3
|
||||
Table with non unique cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|2
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
2
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 4
|
||||
Table with non unique and null cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|
|
||||
4|2
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
|
||||
2
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
CREATE INDEX
|
||||
@@ -132,5 +147,8 @@ NOTICE: Trying to recover data from _cartodb_id0 column
|
||||
|
||||
DROP TABLE
|
||||
SET
|
||||
BEGIN
|
||||
DO
|
||||
ROLLBACK
|
||||
DROP FUNCTION
|
||||
DROP FUNCTION
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
BEGIN
|
||||
CREATE TABLE
|
||||
COPY 3
|
||||
none||
|
||||
only_com_dec|.|,
|
||||
only_dot_dec|,|.
|
||||
|
||||
@@ -2,4 +2,10 @@ WITH data AS (
|
||||
SELECT array_agg(x::numeric) s FROM generate_series(1,300) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT round(unnest(CDB_EqualIntervalBins(s, 7)),7) FROM data
|
||||
SELECT round(unnest(CDB_EqualIntervalBins(s, 7)),7) FROM data;
|
||||
|
||||
WITH data_nulls AS (
|
||||
SELECT array_agg(CASE WHEN x % 2 != 0 THEN x ELSE NULL END::numeric) s FROM generate_series(1,100) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT round(unnest(CDB_EqualIntervalBins(s, 7)),7) FROM data_nulls;
|
||||
|
||||
@@ -5,3 +5,10 @@
|
||||
213.8571429
|
||||
256.4285714
|
||||
299.0000000
|
||||
15.0000000
|
||||
29.0000000
|
||||
43.0000000
|
||||
57.0000000
|
||||
71.0000000
|
||||
85.0000000
|
||||
99.0000000
|
||||
|
||||
10
test/CDB_EstimateRowCountTest.sql
Normal file
10
test/CDB_EstimateRowCountTest.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
SET client_min_messages TO error;
|
||||
\set VERBOSITY terse
|
||||
CREATE TABLE tmptab1(id INT);
|
||||
INSERT INTO tmptab1(id) VALUES (1), (2), (3);
|
||||
CREATE TABLE tmptab2(id INT, value NUMERIC);
|
||||
INSERT INTO tmptab2(id, value) VALUES (1, 10.0), (2, 20.0);
|
||||
SELECT CDB_EstimateRowCount('SELECT SUM(value) FROM tmptab1 INNER JOIN tmptab2 ON (tmptab1.id = tmptab2.id);') AS row_count;
|
||||
SELECT CDB_EstimateRowCount('UPDATE tmptab2 SET value = 30 WHERE id=2;') AS row_count;
|
||||
DROP TABLE tmptab2;
|
||||
DROP TABLE tmptab1;
|
||||
9
test/CDB_EstimateRowCountTest_expect
Normal file
9
test/CDB_EstimateRowCountTest_expect
Normal file
@@ -0,0 +1,9 @@
|
||||
SET
|
||||
CREATE TABLE
|
||||
INSERT 0 3
|
||||
CREATE TABLE
|
||||
INSERT 0 2
|
||||
1
|
||||
1
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
@@ -1,5 +1,11 @@
|
||||
WITH data AS (
|
||||
SELECT array_agg(x) x FROM generate_series(1,100) x
|
||||
SELECT array_agg(x::numeric) s FROM generate_series(1,100) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT round(unnest(CDB_HeadsTailsBins(x, 7)),2) FROM data
|
||||
)
|
||||
SELECT round(unnest(CDB_HeadsTailsBins(s, 7)),2) FROM data;
|
||||
|
||||
WITH data_nulls AS (
|
||||
SELECT array_agg(CASE WHEN x % 2 != 0 THEN x ELSE NULL END::numeric) s FROM generate_series(1,100) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT round(unnest(CDB_HeadsTailsBins(s, 7)),2) FROM data_nulls;
|
||||
|
||||
@@ -5,3 +5,9 @@
|
||||
96.50
|
||||
98.00
|
||||
99.00
|
||||
49.76
|
||||
74.65
|
||||
88.50
|
||||
94.50
|
||||
98.00
|
||||
99.00
|
||||
|
||||
@@ -14,17 +14,17 @@ SELECT * FROM cartodb._CDB_Unique_Identifier(NULL, 'largolargolargolargolargolar
|
||||
SELECT * FROM cartodb._CDB_Unique_Identifier('prefix_', 'largolargolargolargolargolargolargolargolargolargolargolargolar', NULL);
|
||||
|
||||
-- Test new identifier is found when name is taken from previous case
|
||||
CREATE TABLE prefix_largolargolargolargolargolargolargolargolargolargolar (name text);
|
||||
CREATE TABLE prefix_largolargolargolargolargolargolargolargolargolargola (name text);
|
||||
SELECT * FROM cartodb._CDB_Unique_Identifier('prefix_', 'largolargolargolargolargolargolargolargolargolargolargolargolar', NULL);
|
||||
DROP TABLE prefix_largolargolargolargolargolargolargolargolargolargolar;
|
||||
DROP TABLE prefix_largolargolargolargolargolargolargolargolargolargola;
|
||||
|
||||
-- Test unique identifier creation with suffix with long length normal relname
|
||||
SELECT * FROM cartodb._CDB_Unique_Identifier(NULL, 'largolargolargolargolargolargolargolargolargolargolargolargolar', '_suffix');
|
||||
|
||||
-- Test new identifier is found when name is taken from previous case
|
||||
CREATE TABLE largolargolargolargolargolargolargolargolargolargolar_suffix (name text);
|
||||
CREATE TABLE largolargolargolargolargolargolargolargolargolargola_suffix (name text);
|
||||
SELECT * FROM cartodb._CDB_Unique_Identifier(NULL, 'largolargolargolargolargolargolargolargolargolargolargolargolar', '_suffix');
|
||||
DROP TABLE largolargolargolargolargolargolargolargolargolargolar_suffix;
|
||||
DROP TABLE largolargolargolargolargolargolargolargolargolargola_suffix;
|
||||
|
||||
-- Test unique identifier creation with normal length UTF8 relname
|
||||
SELECT * FROM cartodb._CDB_Unique_Identifier(NULL, 'piraña', NULL);
|
||||
@@ -72,7 +72,7 @@ SELECT * FROM cartodb._CDB_Unique_Column_Identifier('prefix_', 'largolargolargol
|
||||
DROP TABLE test;
|
||||
|
||||
-- Test new identifier is found when name is taken from previous case
|
||||
CREATE TABLE test (prefix_largolargolargolargolargolargolargolargolargolargolar text);
|
||||
CREATE TABLE test (prefix_largolargolargolargolargolargolargolargolargolargola text);
|
||||
SELECT * FROM cartodb._CDB_Unique_Column_Identifier('prefix_', 'largolargolargolargolargolargolargolargolargolargolargolargolar', NULL, 'test'::regclass);
|
||||
DROP TABLE test;
|
||||
|
||||
@@ -82,7 +82,7 @@ SELECT * FROM cartodb._CDB_Unique_Column_Identifier(NULL, 'largolargolargolargol
|
||||
DROP TABLE test;
|
||||
|
||||
-- Test new identifier is found when name is taken from previous case
|
||||
CREATE TABLE test (largolargolargolargolargolargolargolargolargolargolar_suffix text);
|
||||
CREATE TABLE test (largolargolargolargolargolargolargolargolargolargola_suffix text);
|
||||
SELECT * FROM cartodb._CDB_Unique_Column_Identifier(NULL, 'largolargolargolargolargolargolargolargolargolargolargolargolar', '_suffix', 'test'::regclass);
|
||||
DROP TABLE test;
|
||||
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
relname
|
||||
prefix_relname
|
||||
relname_suffix
|
||||
largolargolargolargolargolargolargolargolargolargolargolargo
|
||||
prefix_largolargolargolargolargolargolargolargolargolargolar
|
||||
largolargolargolargolargolargolargolargolargolargolargolarg
|
||||
prefix_largolargolargolargolargolargolargolargolargolargola
|
||||
CREATE TABLE
|
||||
prefix_largolargolargolargolargolargolargolargolargolargolar_0
|
||||
prefix_largolargolargolargolargolargolargolargolargolargola0
|
||||
DROP TABLE
|
||||
largolargolargolargolargolargolargolargolargolargolar_suffix
|
||||
largolargolargolargolargolargolargolargolargolargola_suffix
|
||||
CREATE TABLE
|
||||
largolargolargolargolargolargolargolargolargolargolar_suffix_0
|
||||
largolargolargolargolargolargolargolargolargolargola_suffix0
|
||||
DROP TABLE
|
||||
piraña
|
||||
prefix_piraña
|
||||
piraña_suffix
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpiñaácid
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpiñaáci
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi
|
||||
CREATE TABLE
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_0
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi0
|
||||
DROP TABLE
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix
|
||||
CREATE TABLE
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix_0
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix0
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
colname
|
||||
prefix_colname
|
||||
colname_suffix
|
||||
largolargolargolargolargolargolargolargolargolargolargolargo
|
||||
prefix_largolargolargolargolargolargolargolargolargolargolar
|
||||
largolargolargolargolargolargolargolargolargolargolargolarg
|
||||
prefix_largolargolargolargolargolargolargolargolargolargola
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
prefix_largolargolargolargolargolargolargolargolargolargolar_0
|
||||
prefix_largolargolargolargolargolargolargolargolargolargola0
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
largolargolargolargolargolargolargolargolargolargolar_suffix
|
||||
largolargolargolargolargolargolargolargolargolargola_suffix
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
largolargolargolargolargolargolargolargolargolargolar_suffix_0
|
||||
largolargolargolargolargolargolargolargolargolargola_suffix0
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
piraña
|
||||
prefix_piraña
|
||||
piraña_suffix
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpiñaácid
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpiñaáci
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_0
|
||||
prefix_piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi0
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix_0
|
||||
piñaácidpiñaácidpiñaácidpiñaácidpiñaácidpi_suffix0
|
||||
DROP TABLE
|
||||
pira
|
||||
pirañ
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
WITH data AS (
|
||||
SELECT array_agg(x) x FROM generate_series(1,100) x
|
||||
SELECT array_agg(x::numeric) s FROM generate_series(1,300) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT unnest(CDB_JenksBins(x, 7)) FROM data
|
||||
SELECT unnest(CDB_JenksBins(s, 7)) FROM data;
|
||||
|
||||
WITH data_nulls AS (
|
||||
SELECT array_agg(CASE WHEN x % 2 != 0 THEN x ELSE NULL END::numeric) s FROM generate_series(1,300) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT unnest(CDB_JenksBins(s, 7)) FROM data_nulls;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
13
|
||||
29
|
||||
43
|
||||
57
|
||||
71
|
||||
83
|
||||
99
|
||||
86
|
||||
129
|
||||
172
|
||||
213
|
||||
257
|
||||
299
|
||||
37
|
||||
51
|
||||
97
|
||||
157
|
||||
213
|
||||
241
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ SELECT _CDB_Aggregated_Attributes_Expression('base_bare_t'::regclass);
|
||||
SELECT _CDB_Aggregated_Attributes_Expression('base_bare_t'::regclass, 'tab');
|
||||
|
||||
SELECT CDB_CreateOverviews('base_bare_t'::regclass);
|
||||
SELECT count(*) FROM _vovw_5_base_bare_t;
|
||||
SELECT count(*) FROM _vovw_2_base_bare_t;
|
||||
|
||||
|
||||
SELECT _CDB_Aggregable_Attributes_Expression('base_t'::regclass);
|
||||
@@ -16,18 +16,28 @@ SELECT _CDB_Aggregated_Attributes_Expression('base_t'::regclass);
|
||||
SELECT _CDB_Aggregated_Attributes_Expression('base_t'::regclass, 'tab');
|
||||
|
||||
SELECT CDB_CreateOverviews('base_t'::regclass);
|
||||
SELECT count(*) FROM _vovw_5_base_t;
|
||||
SELECT count(*) FROM _vovw_2_base_t;
|
||||
|
||||
SELECT CDB_CreateOverviews('polyg_t'::regclass);
|
||||
|
||||
SELECT CDB_CreateOverviews('column_types_t'::regclass);
|
||||
|
||||
SELECT CDB_Overviews('base_t'::regclass);
|
||||
SELECT CDB_Overviews('"public"."base_t"'::regclass);
|
||||
SELECT CDB_Overviews(ARRAY['base_t'::regclass, 'base_bare_t'::regclass]);
|
||||
SELECT CDB_Overviews('polyg_t'::regclass);
|
||||
SELECT CDB_Overviews('column_types_t'::regclass);
|
||||
|
||||
SELECT CDB_DropOverviews('column_types_t'::regclass);
|
||||
SELECT CDB_DropOverviews('base_bare_t'::regclass);
|
||||
SELECT CDB_DropOverviews('base_t'::regclass);
|
||||
SELECT count(*) FROM _vovw_5_base_t;
|
||||
SELECT count(*) FROM _vovw_2_base_t;
|
||||
|
||||
SELECT CDB_CreateOverviewsWithToleranceInPixels('base_t'::regclass, 7.5);
|
||||
SELECT count(*) FROM _vovw_2_base_t;
|
||||
SELECT CDB_DropOverviews('base_t'::regclass);
|
||||
|
||||
DROP TABLE column_types_t;
|
||||
DROP TABLE base_bare_t;
|
||||
DROP TABLE base_t;
|
||||
DROP TABLE polyg_t;
|
||||
|
||||
@@ -5,40 +5,44 @@ CREATE TABLE
|
||||
INSERT 0 1114
|
||||
CREATE TABLE
|
||||
INSERT 0 5
|
||||
SELECT 1114
|
||||
|
||||
|
||||
|
||||
{_vovw_5_base_bare_t,_vovw_4_base_bare_t,_vovw_3_base_bare_t,_vovw_2_base_bare_t,_vovw_1_base_bare_t,_vovw_0_base_bare_t}
|
||||
125
|
||||
{_vovw_2_base_bare_t,_vovw_1_base_bare_t,_vovw_0_base_bare_t}
|
||||
126
|
||||
number,int_number,name,start
|
||||
AVG(number)::double precision AS number,AVG(int_number)::integer AS int_number,CASE count(*) WHEN 1 THEN MIN(name) ELSE NULL END::text AS name,CASE count(*) WHEN 1 THEN MIN(start) ELSE NULL END::date AS start
|
||||
AVG(tab.number)::double precision AS number,AVG(tab.int_number)::integer AS int_number,CASE count(*) WHEN 1 THEN MIN(tab.name) ELSE NULL END::text AS name,CASE count(*) WHEN 1 THEN MIN(tab.start) ELSE NULL END::date AS start
|
||||
{_vovw_5_base_t,_vovw_4_base_t,_vovw_3_base_t,_vovw_2_base_t,_vovw_1_base_t,_vovw_0_base_t}
|
||||
125
|
||||
SUM(number*1)/count(*)::double precision AS number,SUM(int_number*1)/count(*)::integer AS int_number,CASE WHEN count(distinct name) = 1 THEN MIN(name) WHEN count(*) < 5 THEN string_agg(distinct name,' / ') ELSE '*' END::text AS name,CASE count(*) WHEN 1 THEN MIN(start) ELSE NULL END::date AS start
|
||||
SUM(tab.number*1)/count(*)::double precision AS number,SUM(tab.int_number*1)/count(*)::integer AS int_number,CASE WHEN count(distinct tab.name) = 1 THEN MIN(tab.name) WHEN count(*) < 5 THEN string_agg(distinct tab.name,' / ') ELSE '*' END::text AS name,CASE count(*) WHEN 1 THEN MIN(tab.start) ELSE NULL END::date AS start
|
||||
{_vovw_2_base_t,_vovw_1_base_t,_vovw_0_base_t}
|
||||
126
|
||||
|
||||
{_vovw_2_column_types_t,_vovw_1_column_types_t,_vovw_0_column_types_t}
|
||||
(base_t,0,_vovw_0_base_t)
|
||||
(base_t,1,_vovw_1_base_t)
|
||||
(base_t,2,_vovw_2_base_t)
|
||||
(base_t,0,_vovw_0_base_t)
|
||||
(base_t,1,_vovw_1_base_t)
|
||||
(base_t,2,_vovw_2_base_t)
|
||||
(base_t,3,_vovw_3_base_t)
|
||||
(base_t,4,_vovw_4_base_t)
|
||||
(base_t,5,_vovw_5_base_t)
|
||||
(base_bare_t,0,_vovw_0_base_bare_t)
|
||||
(base_bare_t,1,_vovw_1_base_bare_t)
|
||||
(base_bare_t,2,_vovw_2_base_bare_t)
|
||||
(base_bare_t,3,_vovw_3_base_bare_t)
|
||||
(base_bare_t,4,_vovw_4_base_bare_t)
|
||||
(base_bare_t,5,_vovw_5_base_bare_t)
|
||||
(base_t,0,_vovw_0_base_t)
|
||||
(base_t,1,_vovw_1_base_t)
|
||||
(base_t,2,_vovw_2_base_t)
|
||||
(base_t,3,_vovw_3_base_t)
|
||||
(base_t,4,_vovw_4_base_t)
|
||||
(base_t,5,_vovw_5_base_t)
|
||||
(column_types_t,0,_vovw_0_column_types_t)
|
||||
(column_types_t,1,_vovw_1_column_types_t)
|
||||
(column_types_t,2,_vovw_2_column_types_t)
|
||||
|
||||
|
||||
ERROR: relation "_vovw_5_base_t" does not exist
|
||||
LINE 1: SELECT count(*) FROM _vovw_5_base_t;
|
||||
|
||||
ERROR: relation "_vovw_2_base_t" does not exist
|
||||
LINE 1: SELECT count(*) FROM _vovw_2_base_t;
|
||||
^
|
||||
{_vovw_5_base_t,_vovw_4_base_t,_vovw_3_base_t,_vovw_2_base_t,_vovw_1_base_t,_vovw_0_base_t}
|
||||
38
|
||||
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
WITH data AS (
|
||||
SELECT array_agg(x) x FROM generate_series(1,100) x
|
||||
SELECT array_agg(x::numeric) s FROM generate_series(1,100) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT unnest(CDB_QuantileBins(x, 7)) FROM data
|
||||
SELECT unnest(CDB_QuantileBins(s, 7)) FROM data;
|
||||
|
||||
WITH data_nulls AS (
|
||||
SELECT array_agg(CASE WHEN x % 2 != 0 THEN x ELSE NULL END::numeric) s FROM generate_series(1,100) x
|
||||
WHERE x % 5 != 0 AND x % 7 != 0
|
||||
)
|
||||
SELECT unnest(CDB_QuantileBins(s, 7)) FROM data_nulls;
|
||||
|
||||
@@ -4,4 +4,11 @@
|
||||
57
|
||||
71
|
||||
86
|
||||
99
|
||||
29
|
||||
57
|
||||
87
|
||||
|
||||
|
||||
|
||||
99
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
SET client_min_messages TO error;
|
||||
\set VERBOSITY terse
|
||||
|
||||
WITH q AS ( SELECT CDB_QueryStatements('
|
||||
SELECT * FROM geometry_columns;
|
||||
') as statement )
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
SET
|
||||
1|1|SELECT * FROM geometry_columns
|
||||
2|1|SELECT * FROM geometry_columns
|
||||
3|1|SELECT * FROM geometry_columns
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
SET client_min_messages TO warning;
|
||||
\set VERBOSITY terse
|
||||
|
||||
WITH inp AS ( select 'SELECT * FROM geometry_columns'::text as q )
|
||||
SELECT q, CDB_QueryTables(q) from inp;
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
SET
|
||||
SELECT * FROM geometry_columns|{pg_catalog.pg_attribute,pg_catalog.pg_class,pg_catalog.pg_constraint,pg_catalog.pg_namespace,pg_catalog.pg_type}
|
||||
SELECT a.attname FROM pg_class c JOIN pg_attribute a on (a.attrelid = c.oid)|{pg_catalog.pg_attribute,pg_catalog.pg_class}
|
||||
CREATE table "my'tab;le" as select 1|{}
|
||||
SELECT a.oid, b.oid FROM pg_class a, pg_class b|{pg_catalog.pg_class}
|
||||
SELECT 1 as col1; select 2 as col2|{}
|
||||
WARNING: CDB_QueryTables cannot explain query: select 1 from nonexistant (42P01: relation "nonexistant" does not exist)
|
||||
CONTEXT: PL/pgSQL function cdb_querytables(text) line 3 at RETURN
|
||||
ERROR: relation "nonexistant" does not exist
|
||||
CONTEXT: PL/pgSQL function cdb_querytables(text) line 3 at RETURN
|
||||
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)
|
||||
CONTEXT: PL/pgSQL function cdb_querytables(text) line 3 at RETURN
|
||||
ERROR: relation "test" does not exist
|
||||
CONTEXT: PL/pgSQL function cdb_querytables(text) line 3 at RETURN
|
||||
WITH a AS (select * from pg_class) select * from a|{pg_catalog.pg_class}
|
||||
CREATE SCHEMA
|
||||
CREATE TABLE
|
||||
|
||||
@@ -27,5 +27,21 @@ INSERT INTO big VALUES (8193);
|
||||
SELECT CDB_SetUserQuotaInBytes(0);
|
||||
INSERT INTO big VALUES (8194);
|
||||
DROP TABLE big;
|
||||
|
||||
|
||||
--analysis tables should be excluded from quota:
|
||||
CREATE TABLE big(a int);
|
||||
CREATE TRIGGER test_quota BEFORE UPDATE OR INSERT ON big
|
||||
EXECUTE PROCEDURE CDB_CheckQuota(1, 1, 'public');
|
||||
SELECT CDB_SetUserQuotaInBytes(1);
|
||||
CREATE TABLE analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d4(id int);
|
||||
INSERT INTO analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d4(id) VALUES (1),(2),(3),(4),(5);
|
||||
INSERT INTO big VALUES (1); -- allowed, check runs before
|
||||
DROP TABLE analysis_2f13a3dbd7_41bd92976fc6dd97072afe4ee450054f4c0715d4;
|
||||
INSERT INTO big VALUES (2); -- disallowed, quota exceeds before
|
||||
DROP TABLE big;
|
||||
SELECT CDB_SetUserQuotaInBytes(0);
|
||||
|
||||
|
||||
set client_min_messages to NOTICE;
|
||||
DROP FUNCTION _CDB_UserQuotaInBytes();
|
||||
|
||||
@@ -18,5 +18,15 @@ ERROR: Quota exceeded by 443.998046875KB
|
||||
0
|
||||
INSERT 0 1
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
CREATE TRIGGER
|
||||
1
|
||||
CREATE TABLE
|
||||
INSERT 0 5
|
||||
INSERT 0 1
|
||||
DROP TABLE
|
||||
ERROR: Quota exceeded by 3.9990234375KB
|
||||
DROP TABLE
|
||||
0
|
||||
SET
|
||||
DROP FUNCTION
|
||||
|
||||
@@ -3,4 +3,5 @@ SET SCHEMA 'cartodb';
|
||||
\i scripts-available/CDB_TableMetadata.sql
|
||||
\i scripts-available/CDB_ColumnNames.sql
|
||||
\i scripts-available/CDB_ColumnType.sql
|
||||
SET SCHEMA 'public';
|
||||
\i scripts-available/CDB_AnalysisCatalog.sql
|
||||
SET SCHEMA 'public';
|
||||
|
||||
@@ -172,8 +172,7 @@ function drop_raster_table() {
|
||||
sql ${ROLE} "DROP TABLE ${ROLE}.${TABLENAME};"
|
||||
}
|
||||
|
||||
|
||||
function setup() {
|
||||
function setup_database() {
|
||||
${CMD} -c "CREATE DATABASE ${DATABASE}"
|
||||
sql "CREATE SCHEMA cartodb;"
|
||||
sql "GRANT USAGE ON SCHEMA cartodb TO public;"
|
||||
@@ -182,9 +181,14 @@ function setup() {
|
||||
|
||||
log_info "########################### BOOTSTRAP ###########################"
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_Organizations.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_OverviewsSupport.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_AnalysisSupport.sql
|
||||
# trick to allow forcing a schema when loading SQL files (see: http://bit.ly/1HeLnhL)
|
||||
${CMD} -d ${DATABASE} -f test/extension/run_at_cartodb_schema.sql
|
||||
}
|
||||
|
||||
function setup() {
|
||||
setup_database
|
||||
|
||||
log_info "############################# SETUP #############################"
|
||||
create_role_and_schema cdb_testmember_1
|
||||
@@ -199,6 +203,10 @@ function setup() {
|
||||
sql cdb_testmember_2 'SELECT * FROM cdb_testmember_2.bar;'
|
||||
}
|
||||
|
||||
|
||||
function tear_down_database() {
|
||||
${CMD} -c "DROP DATABASE ${DATABASE}"
|
||||
}
|
||||
function tear_down() {
|
||||
log_info "########################### USER TEAR DOWN ###########################"
|
||||
sql cdb_testmember_1 "SELECT * FROM cartodb.CDB_Organization_Remove_Access_Permission('cdb_testmember_1', 'foo', 'cdb_testmember_2');"
|
||||
@@ -219,9 +227,11 @@ function tear_down() {
|
||||
sql 'DROP ROLE cdb_testmember_1;'
|
||||
sql 'DROP ROLE cdb_testmember_2;'
|
||||
|
||||
${CMD} -c "DROP DATABASE ${DATABASE}"
|
||||
tear_down_database
|
||||
DATABASE=postgres sql postgres 'DROP ROLE IF EXISTS publicuser';
|
||||
}
|
||||
|
||||
|
||||
function run_tests() {
|
||||
local FAILED_TESTS=()
|
||||
|
||||
@@ -343,6 +353,41 @@ function test_cdb_tablemetadatatouch_fails_from_user_without_permission() {
|
||||
sql postgres "REVOKE ALL ON CDB_TableMetadata FROM cdb_testmember_1;"
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch_fully_qualifies_names() {
|
||||
sql postgres "CREATE TABLE touch_invalidations (table_name text);"
|
||||
sql postgres "create or replace function cartodb.cdb_invalidate_varnish(table_name text) returns void as \$\$ begin insert into touch_invalidations select table_name; end; \$\$ language 'plpgsql';"
|
||||
|
||||
#default schema
|
||||
sql "CREATE TABLE touch_example (a int);"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "public.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
sql postgres "DROP TABLE touch_example"
|
||||
|
||||
#setup different schema
|
||||
sql postgres "CREATE SCHEMA test_schema;"
|
||||
sql postgres "CREATE TABLE test_schema.touch_example (a int);"
|
||||
|
||||
#different schema outside search_path
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('test_schema.touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "test_schema.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
|
||||
#different schema in default search_path
|
||||
sql postgres "SET search_path=test_schema,public,cartodb; SELECT CDB_TableMetadataTouch('test_schema.touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "test_schema.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
|
||||
#teardown different schema
|
||||
sql postgres 'DROP TABLE test_schema.touch_example;'
|
||||
sql postgres 'DROP SCHEMA test_schema;'
|
||||
|
||||
|
||||
|
||||
sql postgres 'DROP FUNCTION cartodb.cdb_invalidate_varnish(table_name text);'
|
||||
sql postgres 'DROP TABLE touch_invalidations'
|
||||
}
|
||||
|
||||
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);'
|
||||
@@ -429,6 +474,117 @@ function test_cdb_querytables_happy_cases() {
|
||||
sql postgres 'DROP SCHEMA foo;'
|
||||
}
|
||||
|
||||
function test_foreign_tables() {
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryStatements.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_QueryTables.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_TableMetadata.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_Conf.sql
|
||||
${CMD} -d ${DATABASE} -f scripts-available/CDB_ForeignTable.sql
|
||||
|
||||
|
||||
DATABASE=fdw_target setup_database
|
||||
${CMD} -d fdw_target -f scripts-available/CDB_QueryStatements.sql
|
||||
${CMD} -d fdw_target -f scripts-available/CDB_QueryTables.sql
|
||||
${CMD} -d fdw_target -f scripts-available/CDB_TableMetadata.sql
|
||||
|
||||
DATABASE=fdw_target sql postgres "DO
|
||||
\$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT *
|
||||
FROM pg_catalog.pg_user
|
||||
WHERE usename = 'publicuser') THEN
|
||||
|
||||
CREATE ROLE publicuser LOGIN;
|
||||
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);'
|
||||
DATABASE=fdw_target sql postgres 'CREATE TABLE test_fdw.foo2 (a int);'
|
||||
DATABASE=fdw_target sql postgres 'INSERT INTO test_fdw.foo2 (a) values (42);'
|
||||
DATABASE=fdw_target sql postgres "CREATE USER fdw_user WITH PASSWORD 'foobarino';"
|
||||
DATABASE=fdw_target sql postgres 'GRANT USAGE ON SCHEMA test_fdw TO fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'GRANT SELECT ON TABLE test_fdw.foo TO fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'GRANT SELECT ON TABLE test_fdw.foo2 TO fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'GRANT SELECT ON cdb_tablemetadata_text TO fdw_user;'
|
||||
|
||||
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\"},
|
||||
\"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 n.nspname,
|
||||
c.relname,
|
||||
s.srvname FROM pg_catalog.pg_foreign_table ft
|
||||
INNER JOIN pg_catalog.pg_class c ON c.oid = ft.ftrelid
|
||||
INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
INNER JOIN pg_catalog.pg_foreign_server s ON s.oid = ft.ftserver
|
||||
ORDER BY 1, 2" should "test_fdw|cdb_tablemetadata|test_fdw
|
||||
test_fdw|foo|test_fdw"
|
||||
|
||||
sql postgres "SELECT cartodb.CDB_Get_Foreign_Updated_At('test_fdw.foo'::regclass) < NOW()" should 't'
|
||||
|
||||
sql postgres "SELECT a from test_fdw.foo LIMIT 1;" should 42
|
||||
|
||||
# Check function CDB_QueryTables_Updated_At
|
||||
sql postgres 'CREATE TABLE local (b int);'
|
||||
sql postgres 'INSERT INTO local (b) VALUES (43);'
|
||||
sql postgres "SELECT cdb_tablemetadatatouch('public.local'::regclass);"
|
||||
local query='$query$ SELECT * FROM test_fdw.foo, local $query$::text'
|
||||
sql postgres "SELECT dbname, schema_name, table_name FROM cartodb.CDB_QueryTables_Updated_At(${query}) ORDER BY dbname;" should 'fdw_target|test_fdw|foo
|
||||
test_extension|public|local'
|
||||
sql postgres "SELECT table_name FROM cartodb.CDB_QueryTables_Updated_At(${query}) order by updated_at;" should 'foo
|
||||
local'
|
||||
|
||||
# Check function CDB_Last_Updated_Time
|
||||
sql postgres "SELECT cartodb.CDB_Last_Updated_Time('{test_fdw.foo,public.local}'::text[]) < now()" should 't'
|
||||
sql postgres "SELECT cartodb.CDB_Last_Updated_Time('{test_fdw.foo,public.local}'::text[]) > (now() - interval '1 minute')" should 't'
|
||||
|
||||
# Check we quote names on output as needed (as CDB_QueryTablesText does)
|
||||
sql postgres 'CREATE TABLE "local-table-with-dashes" (c int)';
|
||||
sql postgres 'INSERT INTO "local-table-with-dashes" (c) VALUES (44)';
|
||||
sql postgres "SELECT cdb_tablemetadatatouch('public.local-table-with-dashes'::regclass);"
|
||||
query='$query$ SELECT * FROM test_fdw.foo, local, public."local-table-with-dashes" $query$::text'
|
||||
sql postgres "SELECT dbname, schema_name, table_name FROM cartodb.CDB_QueryTables_Updated_At(${query}) ORDER BY dbname, schema_name, table_name;" should 'fdw_target|test_fdw|foo
|
||||
test_extension|public|local
|
||||
test_extension|public|"local-table-with-dashes"'
|
||||
|
||||
# Check CDB_Last_Updated_Time supports quoted identifiers
|
||||
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'
|
||||
|
||||
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;'
|
||||
DATABASE=fdw_target sql postgres 'REVOKE SELECT ON cdb_tablemetadata_text FROM fdw_user;'
|
||||
DATABASE=fdw_target sql postgres 'DROP ROLE fdw_user;'
|
||||
|
||||
sql postgres "select pg_terminate_backend(pid) from pg_stat_activity where datname='fdw_target';"
|
||||
DATABASE=fdw_target tear_down_database
|
||||
}
|
||||
|
||||
function test_cdb_catalog_basic_node() {
|
||||
DEF="'{\"type\":\"buffer\",\"source\":\"b2db66bc7ac02e135fd20bbfef0fdd81b2d15fad\",\"radio\":10000}'"
|
||||
sql postgres "INSERT INTO cartodb.cdb_analysis_catalog (node_id, analysis_def) VALUES ('1bbc4c41ea7c9d3a7dc1509727f698b7', ${DEF}::json)"
|
||||
sql postgres "SELECT status from cartodb.cdb_analysis_catalog where node_id = '1bbc4c41ea7c9d3a7dc1509727f698b7'" should 'pending'
|
||||
sql postgres "DELETE FROM cartodb.cdb_analysis_catalog"
|
||||
}
|
||||
|
||||
#################################################### TESTS END HERE ####################################################
|
||||
|
||||
run_tests $@
|
||||
|
||||
@@ -2240,3 +2240,10 @@ INSERT INTO polyg_t VALUES
|
||||
(3, 'C', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857)),
|
||||
(4, 'D', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857)),
|
||||
(5, 'E', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857));
|
||||
|
||||
CREATE TABLE column_types_t
|
||||
AS SELECT cartodb_id,
|
||||
the_geom,
|
||||
the_geom_webmercator,
|
||||
(CASE cartodb_id % 2 WHEN 0 THEN 'f' ELSE 't' END)::boolean AS is_odd
|
||||
FROM base_bare_t;
|
||||
|
||||
Reference in New Issue
Block a user