Compare commits

..

1 Commits

Author SHA1 Message Date
Javier Goizueta
b1d03d3a5e Use cell centers, not cluster centroids when grouping points
Fixes #225
2016-04-07 15:34:31 +02:00
142 changed files with 1539 additions and 7607 deletions

View File

@ -1,41 +1,17 @@
language: c
sudo: required
env:
global:
- PGUSER=postgres
- PGDATABASE=postgres
- PGOPTIONS='-c client_min_messages=NOTICE'
addons:
postgresql: 9.3
jobs:
include:
- env: POSTGRESQL_VERSION="11" POSTGIS_VERSION="2.5"
dist: xenial
- env: POSTGRESQL_VERSION="12" POSTGIS_VERSION="2.5"
dist: bionic
- env: POSTGRESQL_VERSION="12" POSTGIS_VERSION="3"
dist: bionic
- env: POSTGRESQL_VERSION="13" POSTGIS_VERSION="3"
dist: bionic
before_install:
- sudo apt-get update
#- sudo apt-get install -q postgresql-9.3-postgis-2.1
- sudo apt-get update
- sudo apt-get install -q postgresql-server-dev-9.3
- sudo apt-get install -q postgresql-plpython-9.3
script:
- sudo apt-get remove postgresql* -y
- sudo apt-get install -y --allow-unauthenticated --no-install-recommends --no-install-suggests postgresql-$POSTGRESQL_VERSION postgresql-client-$POSTGRESQL_VERSION postgresql-server-dev-$POSTGRESQL_VERSION postgresql-common
- if [[ $POSTGRESQL_VERSION == '9.6' ]]; then sudo apt-get install -y postgresql-contrib-9.6; fi;
- sudo apt-get install -y --allow-unauthenticated postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION-scripts postgis
# For pre12, install plpython2. For PG12 install plpython3
- if [[ $POSTGRESQL_VERSION == '11' ]]; then sudo apt-get install -y postgresql-plpython-$POSTGRESQL_VERSION python python-redis; else sudo apt-get install -y postgresql-plpython3-$POSTGRESQL_VERSION python3 python3-redis; fi;
- for i in $(pg_lsclusters | tail -n +2 | awk '{print $1}'); do sudo pg_dropcluster --stop $i main; done;
- sudo rm -rf /etc/postgresql/$POSTGRESQL_VERSION /var/lib/postgresql/$POSTGRESQL_VERSION /var/ramfs/postgresql/$POSTGRESQL_VERSION
- sudo pg_createcluster -u postgres $POSTGRESQL_VERSION main --start -- --auth-local trust --auth-host password
- export PGPORT=$(pg_lsclusters | grep $POSTGRESQL_VERSION | awk '{print $3}')
- make
- sudo make install
- make installcheck
after_failure:
- pg_lsclusters
- cat regression.out
- cat regression.diffs
- echo $PGPORT
- sudo cat /var/log/postgresql/postgresql-$POSTGRESQL_VERSION-main.log
- PGOPTIONS='-c client_min_messages=NOTICE' PGUSER=postgres make installcheck ||
{ cat regression.diffs; false; }

View File

@ -22,18 +22,13 @@ and upgrade of the objects. This means using CREATE OR REPLACE for
the functions, and whatever it takes to check existence of any previous
version of objects in other cases.
When adding a new function or modifying an exiting one make sure that the
[VOLATILITY](https://www.postgresql.org/docs/current/static/xfunc-volatility.html) and [PARALLEL](https://www.postgresql.org/docs/9.6/static/parallel-safety.html) categories are updated accordingly.
Although the extension will usually be installed in the "cartodb" schema, please
use @extschema@ to fully-qualify internal calls to avoid name clashes.
When you use postgis functions or types, please fully-qualify them by using
@postgisschema@ (it's changed to "public" by the install script) to avoid
pg_upgrade issues.
When used as an extension (probably always from version 0.2.0 onwards)
all the objects will be installed in a "cartodb" schema. Take this into
account to fully-qualify internal calls to avoid (possibly dangerous)
name clashes.
Every new feature (as well as bugfixes) should come with a test case,
see the 'Writing testcases' section.
see next section.
Writing testcases
-----------------
@ -67,7 +62,3 @@ 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).

127
Makefile
View File

@ -1,10 +1,9 @@
# cartodb/Makefile
EXTENSION = cartodb
EXTVERSION = 0.37.1
EXTVERSION = 0.15.0
SED = sed
AWK = awk
CDBSCRIPTS = \
scripts-enabled/*.sql \
@ -66,50 +65,6 @@ UPGRADABLE = \
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 \
0.20.0 \
0.21.0 \
0.22.0 \
0.22.1 \
0.22.2 \
0.23.0 \
0.23.1 \
0.23.2 \
0.24.0 \
0.24.1 \
0.25.0 \
0.26.0 \
0.26.1 \
0.27.0 \
0.27.1 \
0.27.2 \
0.28.0 \
0.28.1 \
0.29.0 \
0.30.0 \
0.31.0 \
0.32.0 \
0.33.0 \
0.34.0 \
0.35.0 \
0.36.0 \
0.37.0 \
$(EXTVERSION)dev \
$(EXTVERSION)next \
$(END)
@ -120,6 +75,8 @@ UPGRADES = \
$(SED) 's/$$/--$(EXTVERSION).sql/' | \
$(SED) 's/ /--$(EXTVERSION).sql $(EXTENSION)--/g')
GITDIR=$(shell test -d .git && echo '.git' || cat .git | $(SED) 's/^gitdir: //')
DATA_built = \
$(EXTENSION)--$(EXTVERSION).sql \
$(EXTENSION)--$(EXTVERSION)--$(EXTVERSION)next.sql \
@ -131,27 +88,18 @@ EXTRA_CLEAN = cartodb_version.sql
DOCS = README.md
REGRESS_OLD = $(wildcard test/*.sql)
REGRESS_LEGACY = $(REGRESS_OLD:.sql=)
REGRESS = test/test_setup $(REGRESS_LEGACY)
REGRESS = test_setup $(REGRESS_LEGACY)
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
PG_VERSION := $(shell $(PG_CONFIG) --version | $(AWK) '{split($$2,a,"."); print a[1]}')
PG_12_GE := $(shell [ $(PG_VERSION) -ge 12 ] && echo true)
PLPYTHONU := plpythonu
ifeq ($(PG_12_GE), true)
PLPYTHONU := plpython3u
endif
PGPORT ?= '5432'
PGUSER ?= 'postgres'
$(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/@extschema@/cartodb/g' \
-e 's/@postgisschema@/public/g' \
-e 's/@@plpythonu@@/$(PLPYTHONU)/g' >> $@
$(SED) -e 's/public\./cartodb./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 >> $@
@ -165,10 +113,10 @@ $(EXTENSION)--$(EXTVERSION)--$(EXTVERSION)next.sql: $(EXTENSION)--$(EXTVERSION).
cp $< $@
$(EXTENSION).control: $(EXTENSION).control.in Makefile
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/g' -e 's/@@plpythonu@@/$(PLPYTHONU)/g' $< > $@
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
cartodb_version.sql: cartodb_version.sql.in Makefile
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' -e 's/@extschema@/cartodb/g' -e "s/@postgisschema@/public/g" -e 's/@@plpythonu@@/$(PLPYTHONU)/g' $< > $@
cartodb_version.sql: cartodb_version.sql.in Makefile $(GITDIR)/index
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
# Needed for consistent `echo` results with backslashes
SHELL = bash
@ -177,36 +125,19 @@ legacy_regress: $(REGRESS_OLD) Makefile
mkdir -p sql/test/
mkdir -p expected/test/
mkdir -p results/test/
cat sql/test_setup.sql | \
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' -e 's/@extschema@/cartodb/g' -e "s/@postgisschema@/public/g" -e 's/@@plpythonu@@/$(PLPYTHONU)/g' \
> sql/test/test_setup.sql
cp sql/test_setup_expect expected/test/test_setup.out
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}; \
cat $${f} | \
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' \
-e 's/@extschema@/cartodb/g' \
-e "s/@postgisschema@/public/g" \
-e 's/@@plpythonu@@/$(PLPYTHONU)/g' \
-e 's/@@PGPORT@@/$(PGPORT)/g' \
-e 's/@@PGUSER@@/$(PGUSER)/g' \
>> $${of}; \
exp=expected/test/$${tn}.out; \
echo '\set ECHO none' > $${exp}; \
cat test/$${tn}_expect | \
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' \
-e 's/@extschema@/cartodb/g' \
-e "s/@postgisschema@/public/g" \
-e 's/@@plpythonu@@/$(PLPYTHONU)/g' \
-e 's/@@PGPORT@@/$(PGPORT)/g' \
-e 's/@@PGUSER@@/$(PGUSER)/g' \
>> $${exp}; \
done
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}; \
cat $${f} | \
$(SED) -e 's/public\./cartodb./g' >> $${of}; \
exp=expected/test/$${tn}.out; \
echo '\set ECHO none' > $${exp}; \
cat test/$${tn}_expect >> $${exp}; \
done
test_organization:
bash test/organization/test.sh
@ -214,15 +145,7 @@ test_organization:
test_extension_new:
bash test/extension/test.sh
legacy_tests: legacy_regress $(EXTENSION)--unpackaged--$(EXTVERSION).sql
legacy_tests: legacy_regress
PGREGRESS := $(shell dirname `$(PG_CONFIG) --pgxs`)/../../src/test/regress/pg_regress
PGBINDIR := $(shell $(PG_CONFIG) --bindir)
PGREGRESSDATABASE = 'contrib_regression'
regress: legacy_tests
PGUSER=$(PGUSER) \
PGPORT=$(PGPORT) \
$(PGREGRESS) --inputdir=./ --bindir='$(PGBINDIR)' --dbname=$(PGREGRESSDATABASE) $(REGRESS)
installcheck: legacy_tests test_extension_new test_organization
installcheck: test_extension_new test_organization
$(MAKE) -C . regress

240
NEWS.md
View File

@ -1,241 +1,5 @@
0.37.1 (2020-12-02)
* Change `__CDB_RegenerateTable_Get_Commands` to use the caller timeout or '1min' if not set.
0.37.0 (2020-11-26)
* Raised minimum PG version to 11.
* Add `CDB_RegenerateTable` function to regenerate a table.
* Add `CDB_GetTableQueries` to get the queries of a table (constraints, indices, triggers...).
* Add `CDB_ApplyQueriesSafe` to apply the queries of `CDB_GetTableQueries` discarding any exceptions.
* Deprecate creation of new overview tables.
* _cdb_has_usable_geom_record: Check only the extension schema.
0.36.0 (2020-02-13)
* Make `_CDB_Group_API_Auth` python3 compatible by passing bytes representation instead of a string.
* Make `_CDB_Group_API_Request` python3 compatible by adapting the function signature of `HTTPConnection`.
0.35.0 (2019-12-30)
* Reapply the changes in 0.33.0 (the issue we were looking for was unrelated)
* Reapply `Make PG12 depend on plpython3u instead of plpythonu`
* Fix identifier quotation in `CDB_UserDataSize`
0.34.0 (2019-12-23)
* Revert changes done in 0.33.0, keeping function signature to drop them
0.33.0 (2019-12-20)
* Revert `Make PG12 depend on plpython3u instead of plpythonu`.
* Add functions to manage Federated Tables (Foreign Data Wrapper)
0.32.0 (2019-11-08)
* Fix oAuth ownership re-assignation for functions
* Some fixes for PG12.
* Make PG12 depend on plpython3u instead of plpythonu
* CDB_UserDataSize is now compatible with postgis 3 without postgis_raster.
* Makefile: Add regress target (checks regress tests without needing to install the extension)
0.31.0 (2019-10-08)
* Ghost tables: Add missing tags (#370)
* Set search_path in security definer functions.
0.30.0 (2019-07-17)
* Added new admin functions to connect CARTO with user FDW's (#369)
0.29.0 (2019-07-15)
* Added new function CDB_OAuth:
* Install event trigger to check for table/view/sequence/function creation
* Reassign the ownership of new objects to a defined role in the cdb_conf
* Changed MakeFile to support different expects for differents PG versions
0.28.1 (2019-07-04)
* Avoid temporary tables creation in CDB_SyncTable (#366)
* Make CDB_Get_Foreign_Updated_At robust to missing CDB_TableMetadata (#362)
0.28.0 (2019-07-01)
* New function CDB_SyncTable (#355)
0.27.2 (2019-06-21)
* Improvements and fixes in Ghost tables functions (#360)
0.27.1 (2019-06-03)
* Add some qualifications that were left in the previous release.
0.27.0 (2019-06-03)
* Fully qualify function calls
* Several improvements to bash tests.
* Avoid dropping publicuser in tests.
* Raise minimum requirement to PostgreSQL 9.6.
0.26.1 (2019-03-19)
* Remove default TIS values from Ghost tables functions
0.26.0 (2019-03-11)
* Use `ST_ShiftLongitude` instead of `ST_Shift_Longitude`.
* Add Ghost tables functions to install triggers and enqueue the linking process
0.25.0 (2019-02-22)
* Add `CDB_Username` to get the cartodb username from the current PostgreSQL user
0.24.1 (2019-01-02)
* Drop functions removed in 0.12 (#341)
* Travis: Test with PostgreSQL 9.5, 10 and 11.
0.24.0 (2018-09-13)
* Travis: Test with PostgreSQL 9.5 and 10.
* _cdb_estimated_extent: Fix bug with ST_EstimatedExtent interaction.
* Improvements in `CDB_JenksBins`.
* Now it ignores NULLs.
* No longer puts the same value in multiple categories.
* Removes all limits related to size.
* If not set, the number of iterations done is based now on the size of the array.
* Fixed multiple bugs.
* The internal function `CDB_JenksBinsIteration` has changed its signature.
0.23.2 (2018-07-19)
* Fix `CDB_QueryTablesText` with parenthesized queries (#335)
0.23.1 (2018-07-19)
* Fix `CDB_EstimateRowCount` parallelizability #333
0.23.0 (2018-07-03)
* Add a new helper function `_CDB_Table_Exists(table_name_with_optional_schema TEXT)` #332
0.22.2 (2018-05-29)
* Fix: Fix hyphenates usernames in 0.22.1 fix (#331)
0.22.1 (2018-05-29)
* Fix: Correctly grant permission to all sequences related with table (#330)
0.22.0 (2018-03-22)
* Fix: allow older ogr2ogr to work in -append mode (#319,#325)
* Refactors CDB_QuantileBins to rely on PostgreSQL function `percentile_disc` #316
0.21.0 (2018-02-15)
* Add optional parameter to limit the number of cells in grid-generation functions #322
* Fix: grant usage on cartodb_id sequence when sharing read write #323
* Fix: Change sed in-place for tmpfiles 524319
0.20.0 (2017-11-08)
* Added VOLATILITY and PARALLEL categories to all functions
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)
@ -244,7 +8,7 @@
[#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)

View File

@ -1,7 +1,8 @@
cartodb-postgresql
==================
[![Build Status](http://api.travis-ci.org/CartoDB/cartodb-postgresql.svg?branch=master)](http://travis-ci.org/CartoDB/cartodb-postgresql)
[![Build Status](http://api.travis-ci.org/CartoDB/cartodb-postgresql.svg?branch=master)]
(http://travis-ci.org/CartoDB/cartodb-postgresql)
PostgreSQL extension for CartoDB
@ -10,9 +11,8 @@ See [the cartodb-postgresql wiki](https://github.com/CartoDB/cartodb-postgresql/
Dependencies
------------
* PostgreSQL 11+ (with plpythonu extension). For PostgreSQL 12+ plpython3u is required instead. Older versions might still work but they aren't actively tested or supported.
* [PostGIS extension](http://postgis.net)
* Python with [Redis module](https://pypi.org/project/redis/)
* PostgreSQL 9.3+ (with plpythonu extension and xml support)
* [PostGIS extension](http://postgis.net)
Install
-------
@ -30,7 +30,7 @@ make installcheck
NOTE: you need to run the installcheck as a superuser, use PGUSER
env variable if needed, like: PGUSER=postgres make installcheck
NOTE: the tests need to run against a **clean postgres instance**, if you have some roles already created test will likely fail due `publicuser` not being dropped.
Enable database
@ -39,7 +39,23 @@ Enable database
In a database that needs to be turned into a "cartodb" user database, run:
```sql
CREATE EXTENSION cartodb CASCADE;
CREATE EXTENSION postgis;
CREATE EXTENSION cartodb;
```
Migrate existing cartodb database
---------------------------------
When upgrading an existing cartodb user database, the cartodb extension
can be migrated from the "unpackaged" version. The procedure will copy
the data from ``public.CDB_TableMetada`` to ``cartodb.CDB_TableMetadata``,
re-cartodbfy all tables using old functions in triggers and drop the
cartodb functions from the 'public' schema. All new cartodb objects will
be in the "cartodb" schema.
```sql
CREATE EXTENSION postgis FROM unpackaged;
CREATE EXTENSION cartodb FROM unpackaged;
```
Update cartodb extension

View File

@ -1,11 +0,0 @@
{
"name": "carto_postgresql_ext",
"current_version": {
"requires": {
"postgresql": ">=10.0.0",
"postgis": ">=2.4.0.0"
},
"works_with": {
}
}
}

View File

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

View File

@ -1,6 +1,6 @@
DO $$ BEGIN IF EXISTS (SELECT * FROM pg_proc p, pg_namespace n WHERE p.proname = 'cdb_transformtowebmercator' AND p.pronamespace = n.oid AND n.nspname = 'public') THEN RAISE EXCEPTION 'Use CREATE EXTENSION cartodb FROM unpackaged'; END IF; END; $$ LANGUAGE 'plpgsql'; -- forbid duplicated extension
CREATE OR REPLACE FUNCTION @extschema@.CDB_version()
CREATE OR REPLACE FUNCTION cartodb.CDB_version()
RETURNS text AS $$
SELECT '@@VERSION@@'::text;
$$ language 'sql' IMMUTABLE STRICT;

View File

@ -1,25 +0,0 @@
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.

View File

@ -1,31 +1,19 @@
============================ WARNING ===================================
Creating overviews is no longer supported.
============================ WARNING ===================================
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 havig a fewer number of records
The *reduction* consists in 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` function.
Overviews are created through the `CDB_CreateOverviews`.
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` 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
The `CDB_DropOverviews` remove a dataset's existing overviews.
### CDB_CreateOverviews
@ -63,14 +51,10 @@ CDB_CreateOverviews(table_name, ref_z_strategy, reduction_strategy)
#### 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)
be specified as a tolerance in pixels (if different from the default of 2 pixels)
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).
@ -95,7 +79,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 |

View File

@ -1,56 +0,0 @@
Synchronize two tables. This function will synchronize a *destination* table with a *source* table.
The idea is that the *destination* is a replica of *source* and *source* has been subject to
modifications that are to be applied to *destination*.
This will be achieved by deleting the rows in the destination not present
in the source, inserting rows of the source not in the destination and updating modified rows.
If the destination table does not exist it will be created and all the rows of the source inserted into it.
Both tables must have a consistent `cartodb_id` primary key column which will be used to match
the source and destination rows.
Note that both tables do not necessarily become identical after the synchronization, since additional columns
may have been added to the destination; those columns will not be altered by the synchronization.
In addition some source columns may be skipped by listing them in the optional last argument; such columns
will not be updated in the destination, so if they are present in it their values won't be altered.
#### Using the function
Import some data using COPY FROM into a temporary table, then synchronize a table with the data and
finally delete the temporary table. This could be used import and update some data periodically while
allowing to add columns to the data that will be preserved across updates.
```sql
CREATE tmp_pois(cartodb_id int, name text, type text, longitude double precision, latitude double precision, rank int);
COPY tmp_pois FROM '/tmp/pois.csv';
SELECT CDB_SyncTable('tmp_pois', 'public', 'pois');
DROP TABLE tmp_pois;
```
Now we could perform some changes to the `pois` to maintain our own ranking:
```sql
UPDATE pois SET rank = random()*4 + 1;
```
Then, if the source were updated at `/tmp/pois.csv` we could synchronize with it while preserving our `rank` values with:
```sql
CREATE tmp_pois(cartodb_id int, name text, type text, longitude double precision, latitude double precision, rank int);
COPY tmp_pois FROM '/tmp/pois.csv';
SELECT CDB_SyncTable('tmp_pois', 'public', 'pois', '{rank}');
DROP TABLE tmp_pois;
```
#### Arguments
```
CDB_SyncTable(src_table, dst_schema, dst_table, skip_cols)
```
* **src_table** REGCLASS the source data for the synchronization
* **dst_scgena** REGNAMESPACE the destination schema
* **dst_table** NAME the destination table to be updated
* **skip_cols** NAME[] an array of column names, empty by default, which will be skipped

View File

@ -3,11 +3,7 @@ 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.
--- 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.
--- Returns a row for each table having given permission with the table name
--- Currently accepted permissions are: 'public', 'private' or 'all'
SELECT CDB_UserTables(perms)
```

View File

@ -33,7 +33,7 @@ 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
.. _CartoDB User Table documentation: https://github.com/CartoDB/cartodb-postgresql/blob/master/doc/CartoDB-user-table.md
for further information.
High level requirements

View File

@ -1,5 +1,6 @@
SET client_min_messages TO error;
CREATE EXTENSION cartodb CASCADE;
CREATE EXTENSION postgis;
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE FUNCTION public.cdb_invalidate_varnish(table_name text)
RETURNS void AS $$
BEGIN

View File

@ -1,95 +0,0 @@
-- Table to register analysis nodes from https://github.com/cartodb/camshaft
CREATE TABLE IF NOT EXISTS
@extschema@.cdb_analysis_catalog (
-- md5 hex hash
node_id char(40) CONSTRAINT cdb_analysis_catalog_pkey PRIMARY KEY,
-- being json allows to do queries like analysis_def->>'type' = 'buffer'
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('@extschema@.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 @extschema@.cdb_analysis_catalog ADD COLUMN last_modified_by uuid;
EXCEPTION
WHEN duplicate_column THEN END;
END;
$$;
DO $$
BEGIN
BEGIN
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN last_error_message text;
EXCEPTION
WHEN duplicate_column THEN END;
END;
$$;
DO $$
BEGIN
BEGIN
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN cache_tables regclass[] NOT NULL DEFAULT '{}';
EXCEPTION
WHEN duplicate_column THEN END;
END;
$$;
DO $$
BEGIN
BEGIN
ALTER TABLE @extschema@.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='@extschema@' AND column_name='username' INTO column_index;
IF column_index = 1 OR column_index = 10 THEN
ALTER TABLE @extschema@.cdb_analysis_catalog ADD COLUMN username_final text;
UPDATE @extschema@.cdb_analysis_catalog SET username_final = username;
ALTER TABLE @extschema@.cdb_analysis_catalog DROP COLUMN username;
ALTER TABLE @extschema@.cdb_analysis_catalog RENAME COLUMN username_final TO username;
END IF;
END;
$$;

View File

@ -1,65 +0,0 @@
-- Read configuration parameter analysis_quota_factor, making it
-- accessible to regular users (which don't have access to cdb_conf)
CREATE OR REPLACE FUNCTION @extschema@._CDB_GetConfAnalysisQuotaFactor()
RETURNS float8 AS
$$
BEGIN
RETURN @extschema@.CDB_Conf_GetConf('analysis_quota_factor')::text::float8;
END;
$$ LANGUAGE 'plpgsql'
STABLE
PARALLEL SAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Get the factor (fraction of the quota) for Camshaft cached analysis tables
CREATE OR REPLACE FUNCTION @extschema@._CDB_AnalysisQuotaFactor()
RETURNS float8 AS
$$
DECLARE
factor float8;
BEGIN
-- We use a floating point cdb_conf parameter
factor := @extschema@._CDB_GetConfAnalysisQuotaFactor();
-- With a default value
IF factor IS NULL THEN
factor := 2;
END IF;
RETURN factor;
END;
$$
LANGUAGE 'plpgsql' STABLE PARALLEL SAFE;
-- 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 @extschema@.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 * @extschema@._CDB_AnalysisQuotaFactor() < @extschema@._CDB_AnalysisDataSize(schema_name) THEN
-- The limit is defined by a factor applied to the total space quota for the user
RAISE EXCEPTION 'Analysis cache space limits exceeded';
END IF;
END;
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -1,55 +0,0 @@
-- 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 @extschema@._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 PARALLEL SAFE;
-- 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 @extschema@._CDB_AnalysisTablesInSchema(schema_name text DEFAULT NULL)
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
AS $$
SELECT * FROM @extschema@._CDB_UserTablesInSchema(schema_name) WHERE @extschema@._CDB_IsAnalysisTableName(table_name);
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
-- This function returns a relation user tables excluding analysis tables
-- 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 @extschema@._CDB_NonAnalysisTablesInSchema(schema_name text DEFAULT NULL)
RETURNS TABLE(table_regclass REGCLASS, schema_name TEXT, table_name TEXT)
AS $$
SELECT * FROM @extschema@._CDB_UserTablesInSchema(schema_name) WHERE Not @extschema@._CDB_IsAnalysisTableName(table_name);
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
-- Total spaced used up by Camshaft cached analysis tables in the given schema.
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_AnalysisDataSize(schema_name TEXT DEFAULT NULL)
RETURNS bigint AS
$$
DECLARE
total_size bigint;
BEGIN
WITH analysis_tables AS (
SELECT t.schema_name, t.table_name FROM @extschema@._CDB_AnalysisTablesInSchema(schema_name) t
)
SELECT COALESCE(INT8(SUM(@extschema@._CDB_total_relation_size(analysis_tables.schema_name, analysis_tables.table_name))))::int8
INTO total_size FROM analysis_tables;
IF total_size IS NOT NULL THEN
RETURN total_size;
ELSE
RETURN 0;
END IF;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,18 @@
-- Function returning the column names of a table
CREATE OR REPLACE FUNCTION @extschema@.CDB_ColumnNames(REGCLASS)
CREATE OR REPLACE FUNCTION CDB_ColumnNames(REGCLASS)
RETURNS SETOF information_schema.sql_identifier
AS $$
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 STABLE PARALLEL SAFE;
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;
$$ LANGUAGE SQL;
-- This is to migrate from pre-0.2.0 version
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
GRANT EXECUTE ON FUNCTION @extschema@.CDB_ColumnNames(REGCLASS) TO PUBLIC;
GRANT EXECUTE ON FUNCTION CDB_ColumnNames(REGCLASS) TO PUBLIC;

View File

@ -1,16 +1,18 @@
-- Function returning the type of a column
CREATE OR REPLACE FUNCTION @extschema@.CDB_ColumnType(REGCLASS, TEXT)
CREATE OR REPLACE FUNCTION CDB_ColumnType(REGCLASS, TEXT)
RETURNS information_schema.character_data
AS $$
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 STABLE PARALLEL SAFE;
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;
$$ LANGUAGE SQL;
-- This is to migrate from pre-0.2.0 version
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
GRANT EXECUTE ON FUNCTION @extschema@.CDB_ColumnType(REGCLASS, TEXT) TO public;
GRANT EXECUTE ON FUNCTION CDB_ColumnType(REGCLASS, TEXT) TO public;

View File

@ -5,44 +5,44 @@
-- Functions needing reading configuration should use SECURITY DEFINER.
----------------------------------
-- This will trigger NOTICE if @extschema@.CDB_CONF already exists
-- This will trigger NOTICE if cartodb.CDB_CONF already exists
DO LANGUAGE 'plpgsql' $$
BEGIN
CREATE TABLE IF NOT EXISTS @extschema@.CDB_CONF ( KEY TEXT PRIMARY KEY, VALUE JSON NOT NULL );
CREATE TABLE IF NOT EXISTS cartodb.CDB_CONF ( KEY TEXT PRIMARY KEY, VALUE JSON NOT NULL );
END
$$;
-- This can only be called from an SQL script executed by CREATE EXTENSION
DO LANGUAGE 'plpgsql' $$
BEGIN
PERFORM pg_catalog.pg_extension_config_dump('@extschema@.CDB_CONF', '');
PERFORM pg_catalog.pg_extension_config_dump('cartodb.CDB_CONF', '');
END
$$;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Conf_SetConf(key text, value JSON)
FUNCTION cartodb.CDB_Conf_SetConf(key text, value JSON)
RETURNS void AS $$
BEGIN
PERFORM @extschema@.CDB_Conf_RemoveConf(key);
EXECUTE 'INSERT INTO @extschema@.CDB_CONF (KEY, VALUE) VALUES ($1, $2);' USING key, value;
PERFORM cartodb.CDB_Conf_RemoveConf(key);
EXECUTE 'INSERT INTO cartodb.CDB_CONF (KEY, VALUE) VALUES ($1, $2);' USING key, value;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Conf_RemoveConf(key text)
FUNCTION cartodb.CDB_Conf_RemoveConf(key text)
RETURNS void AS $$
BEGIN
EXECUTE 'DELETE FROM @extschema@.CDB_CONF WHERE KEY = $1;' USING key;
EXECUTE 'DELETE FROM cartodb.CDB_CONF WHERE KEY = $1;' USING key;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Conf_GetConf(key text)
FUNCTION cartodb.CDB_Conf_GetConf(key text)
RETURNS JSON AS $$
DECLARE
value JSON;
BEGIN
EXECUTE 'SELECT VALUE FROM @extschema@.CDB_CONF WHERE KEY = $1;' INTO value USING key;
EXECUTE 'SELECT VALUE FROM cartodb.CDB_CONF WHERE KEY = $1;' INTO value USING key;
RETURN value;
END
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
$$ LANGUAGE PLPGSQL STABLE;

View File

@ -1,14 +0,0 @@
--
-- Legacy file
-- Introduced again to make sure that updates do not leave dangling functions
--
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_create_table();
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_drop_table();
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_alter_column();
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_drop_column();
DROP FUNCTION IF EXISTS @extschema@.cdb_handle_add_column();
DROP FUNCTION IF EXISTS @extschema@.cdb_disable_ddl_hooks();
DROP FUNCTION IF EXISTS @extschema@.cdb_enable_ddl_hooks();

View File

@ -1,6 +1,6 @@
-- Convert timestamp to double precision
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_DateToNumber(input timestamp)
CREATE OR REPLACE FUNCTION CDB_DateToNumber(input timestamp)
RETURNS double precision AS $$
DECLARE output double precision;
BEGIN
@ -12,11 +12,11 @@ BEGIN
RETURN output;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' STABLE STRICT;
-- Convert timestamp with time zone to double precision
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_DateToNumber(input timestamp with time zone)
CREATE OR REPLACE FUNCTION CDB_DateToNumber(input timestamp with time zone)
RETURNS double precision AS $$
DECLARE output double precision;
BEGIN
@ -28,4 +28,4 @@ BEGIN
RETURN output;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' STABLE STRICT;

View File

@ -1,5 +1,5 @@
-- Find thousand and decimal digits separators
CREATE OR REPLACE FUNCTION @extschema@.CDB_DigitSeparator (rel REGCLASS, fld TEXT, OUT t CHAR, OUT d CHAR)
CREATE OR REPLACE FUNCTION CDB_DigitSeparator (rel REGCLASS, fld TEXT, OUT t CHAR, OUT d CHAR)
as $$
DECLARE
sql TEXT;
@ -50,4 +50,4 @@ BEGIN
END
$$
LANGUAGE 'plpgsql' STABLE STRICT PARALLEL SAFE;
LANGUAGE 'plpgsql' STABLE STRICT;

View File

@ -10,7 +10,7 @@
-- 1. width_bucket/histograms: http://tapoueh.org/blog/2014/02/21-PostgreSQL-histogram
-- 2. R implementation: https://github.com/cran/agrmt
CREATE OR REPLACE FUNCTION @extschema@.CDB_DistType ( in_array NUMERIC[] ) RETURNS text as $$
CREATE OR REPLACE FUNCTION CDB_DistType ( in_array NUMERIC[] ) RETURNS text as $$
DECLARE
element_count INT4;
minv numeric;
@ -60,16 +60,16 @@ BEGIN
i := i + 1;
END LOOP;
signature = @extschema@._CDB_DistTypeClassify(ajus);
signature = _CDB_DistTypeClassify(ajus);
END IF;
RETURN signature;
END;
$$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;
-- Classify data into AJUSFL
CREATE OR REPLACE FUNCTION @extschema@._CDB_DistTypeClassify ( in_array INT[] ) RETURNS text as $$
CREATE OR REPLACE FUNCTION _CDB_DistTypeClassify ( in_array INT[] ) RETURNS text as $$
DECLARE
element_count INT4;
maxv numeric;
@ -119,4 +119,4 @@ BEGIN
RETURN type;
END;
$$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;

View File

@ -5,7 +5,7 @@
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_DistinctMeasure ( in_array text[], threshold numeric DEFAULT null ) RETURNS numeric as $$
CREATE OR REPLACE FUNCTION CDB_DistinctMeasure ( in_array text[], threshold numeric DEFAULT null ) RETURNS numeric as $$
DECLARE
element_count INT4;
maxval numeric;
@ -43,4 +43,4 @@ BEGIN
RETURN passes;
END;
$$ language plpgsql IMMUTABLE PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;

View File

@ -1,8 +1,8 @@
--
-- Calculate the equal interval bins for a given column
--
-- @param in_array An array of numbers to determine the best
-- bin boundary
-- @param in_array A numeric array of numbers to determine the best
-- to determine the bin boundary
--
-- @param breaks The number of bins you want to find.
--
@ -11,14 +11,27 @@
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_EqualIntervalBins ( in_array anyarray, breaks INT ) RETURNS anyarray as $$
WITH stats AS (
SELECT min(e), (max(e)-min(e))/breaks AS del
FROM (SELECT unnest(in_array) e) AS p)
SELECT array_agg(bins)
FROM (
SELECT min + generate_series(1,breaks)*del AS bins
FROM stats) q;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
DROP FUNCTION IF EXISTS @extschema@.CDB_EqualIntervalBins( numeric[], integer);
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;

View File

@ -1,36 +0,0 @@
-- Internal function to generate stats for a table if they don't exist
CREATE OR REPLACE FUNCTION @extschema@._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
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Return a row count estimate of the result of a query using statistics
CREATE OR REPLACE FUNCTION @extschema@.CDB_EstimateRowCount(query text)
RETURNS Numeric
AS $$
DECLARE
plan JSON;
BEGIN
-- Make sure statistics exist for all the tables of the query
PERFORM @extschema@._CDB_GenerateStats(tabname) FROM unnest(@extschema@.CDB_QueryTablesText(query)) AS tabname;
-- Use the query planner to obtain an estimate of the number of result rows
EXECUTE 'EXPLAIN (FORMAT JSON) ' || query INTO STRICT plan;
RETURN plan->0->'Plan'->'Plan Rows';
END
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;

View File

@ -1,2 +1,2 @@
SELECT pg_catalog.pg_extension_config_dump('@extschema@.cdb_tablemetadata','');
SELECT pg_catalog.pg_extension_config_dump('cartodb.cdb_tablemetadata','');

View File

@ -1,20 +1,20 @@
CREATE OR REPLACE FUNCTION @extschema@.cdb_extension_reload() RETURNS void
CREATE OR REPLACE FUNCTION cartodb.cdb_extension_reload() RETURNS void
AS $$
DECLARE
ver TEXT;
sql TEXT;
BEGIN
ver := split_part(@extschema@.cdb_version(), ' ', 1);
ver := split_part(cartodb.cdb_version(), ' ', 1);
sql := 'ALTER EXTENSION cartodb UPDATE TO ''' || ver || 'next''';
EXECUTE sql;
sql := 'ALTER EXTENSION cartodb UPDATE TO ''' || ver || '''';
EXECUTE sql;
END;
$$ language 'plpgsql' VOLATILE PARALLEL UNSAFE;
$$ language 'plpgsql' VOLATILE;
CREATE OR REPLACE FUNCTION @extschema@.schema_exists(schema_name text)
CREATE OR REPLACE FUNCTION cartodb.schema_exists(schema_name text)
RETURNS boolean AS
$$
SELECT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = schema_name::text);
$$
language sql STABLE PARALLEL SAFE;
language sql VOLATILE;

View File

@ -1,437 +0,0 @@
--------------------------------------------------------------------------------
-- Private functions
--------------------------------------------------------------------------------
--
-- This function is just a placement to store and use the pattern for
-- foreign object names
-- Servers: cdb_fs_$(server_name)
-- View schema: cdb_fs_$(server_name)
-- > This is where all views created when importing tables are placed
-- > One server has only one view schema
-- Import Schemas: cdb_fs_schema_$(md5sum(server_name || remote_schema_name))
-- > This is where the foreign tables are placed
-- > One server has one import schema per remote schema plus auxiliar ones used
-- to access the remote catalog (pg_catalog, information_schema...)
-- Owner role: cdb_fs_$(md5sum(current_database() || server_name)
-- > This is the role than owns all schemas and tables related to the server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Name_Pattern()
RETURNS TEXT
AS $$
SELECT 'cdb_fs_'::text;
$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--
-- Produce a valid DB name for servers generated for the Federated Server
-- If check_existence is true, it'll throw if the server doesn't exists
-- This name is also used as the schema to store views
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Generate_Server_Name(input_name TEXT, check_existence BOOL)
RETURNS NAME
AS $$
DECLARE
internal_server_name text := format('%s%s', @extschema@.__CDB_FS_Name_Pattern(), input_name);
BEGIN
IF input_name IS NULL OR char_length(input_name) = 0 THEN
RAISE EXCEPTION 'Server name cannot be NULL';
END IF;
-- We discard anything that would be truncated
IF (char_length(internal_server_name) >= 64) THEN
RAISE EXCEPTION 'Server name (%) is too long to be used as identifier', input_name;
END IF;
IF (check_existence AND (NOT EXISTS (SELECT * FROM pg_foreign_server WHERE srvname = internal_server_name))) THEN
RAISE EXCEPTION 'Server "%" does not exist', input_name;
END IF;
RETURN internal_server_name::name;
END
$$
LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
--
-- Given the internal name for a remote server, it returns the name used by the user
-- Reverses __CDB_FS_Generate_Server_Name
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Extract_Server_Name(internal_server_name NAME)
RETURNS TEXT
AS $$
SELECT right(internal_server_name,
char_length(internal_server_name::TEXT) - char_length(@extschema@.__CDB_FS_Name_Pattern()))::TEXT;
$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--
-- Produce a valid name for a schema generated for the Federated Server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Generate_Schema_Name(internal_server_name NAME, schema_name TEXT)
RETURNS NAME
AS $$
DECLARE
hash_value text := md5(internal_server_name::text || '__' || schema_name::text);
BEGIN
IF schema_name IS NULL THEN
RAISE EXCEPTION 'Schema name cannot be NULL';
END IF;
RETURN format('%s%s%s', @extschema@.__CDB_FS_Name_Pattern(), 'schema_', hash_value)::name;
END
$$
LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
--
-- Produce a valid name for a role generated for the Federated Server
-- This needs to include the current database in its hash to avoid collisions in clusters with more than one database
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Generate_Server_Role_Name(internal_server_name NAME)
RETURNS NAME
AS $$
DECLARE
hash_value text := md5(current_database()::text || '__' || internal_server_name::text);
role_name text := format('%s%s%s', @extschema@.__CDB_FS_Name_Pattern(), 'role_', hash_value);
BEGIN
RETURN role_name::name;
END
$$
LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
--
-- Creates (if not exist) a schema to place the objects for a remote schema
-- The schema is with the same AUTHORIZATION as the server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Create_Schema(internal_server_name NAME, schema_name TEXT)
RETURNS NAME
AS $$
DECLARE
schema_name name := @extschema@.__CDB_FS_Generate_Schema_Name(internal_server_name, schema_name);
role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(internal_server_name);
BEGIN
-- By changing the local role to the owner of the server we have an
-- easy way to check for permissions and keep all objects under the same owner
BEGIN
EXECUTE 'SET LOCAL ROLE ' || quote_ident(role_name);
EXCEPTION
WHEN invalid_parameter_value THEN
RAISE EXCEPTION 'Server "%" does not exist',
@extschema@.__CDB_FS_Extract_Server_Name(internal_server_name);
WHEN OTHERS THEN
RAISE EXCEPTION 'Not enough permissions to access the server "%"',
@extschema@.__CDB_FS_Extract_Server_Name(internal_server_name);
END;
IF NOT EXISTS (SELECT oid FROM pg_namespace WHERE nspname = schema_name) THEN
EXECUTE 'CREATE SCHEMA ' || quote_ident(schema_name) || ' AUTHORIZATION ' || quote_ident(role_name);
END IF;
RETURN schema_name;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Returns the type of a server by internal name
-- Currently all of them should be postgres_fdw
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_server_type(internal_server_name NAME)
RETURNS name
AS $$
SELECT f.fdwname
FROM pg_foreign_server s
JOIN pg_foreign_data_wrapper f ON s.srvfdw = f.oid
WHERE s.srvname = internal_server_name;
$$
LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
--
-- Take a config jsonb and transform it to an input suitable for _CDB_SetUp_User_PG_FDW_Server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_credentials_to_user_mapping(input_config JSONB)
RETURNS jsonb
AS $$
DECLARE
mapping jsonb := '{}'::jsonb;
BEGIN
IF NOT (input_config ? 'credentials') THEN
RAISE EXCEPTION 'Credentials are mandatory';
END IF;
-- For now, allow not passing username or password
IF input_config->'credentials'->'username' IS NOT NULL THEN
mapping := jsonb_build_object('user', input_config->'credentials'->'username');
END IF;
IF input_config->'credentials'->'password' IS NOT NULL THEN
mapping := mapping || jsonb_build_object('password', input_config->'credentials'->'password');
END IF;
RETURN (input_config - 'credentials')::jsonb || jsonb_build_object('user_mapping', mapping);
END
$$
LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- Take a config jsonb as input and return it augmented with default
-- options
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_add_default_options(input_config jsonb)
RETURNS jsonb
AS $$
DECLARE
default_options jsonb := '{
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
}';
server_config jsonb;
BEGIN
IF NOT (input_config ? 'server') THEN
RAISE EXCEPTION 'Server information is mandatory';
END IF;
server_config := default_options || to_jsonb(input_config->'server');
RETURN jsonb_set(input_config, '{server}'::text[], server_config);
END
$$
LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- Given an server name, returns the username used in the configuration if the caller has rights to access it
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_get_usermapping_username(internal_server_name NAME)
RETURNS text
AS $$
DECLARE
role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(internal_server_name);
username text;
BEGIN
BEGIN
EXECUTE 'SET LOCAL ROLE ' || quote_ident(role_name);
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
SELECT (SELECT option_value FROM pg_options_to_table(u.umoptions) WHERE option_name LIKE 'user') as name INTO username
FROM pg_foreign_server s
LEFT JOIN pg_user_mappings u
ON u.srvid = s.oid
WHERE s.srvname = internal_server_name
ORDER BY 1;
RESET ROLE;
RETURN username;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Public functions
--------------------------------------------------------------------------------
--
-- Registers a new PG server
--
-- Example config: '{
-- "server": {
-- "dbname": "fdw_target",
-- "host": "localhost",
-- "port": 5432,
-- "extensions": "postgis",
-- "updatable": "false",
-- "use_remote_estimate": "true",
-- "fetch_size": "1000"
-- },
-- "credentials": {
-- "username": "fdw_user",
-- "password": "foobarino"
-- }
-- }'
--
-- The configuration from __CDB_FS_add_default_options will be appended
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_Register_PG(server TEXT, config JSONB)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => false);
final_config json := @extschema@.__CDB_FS_credentials_to_user_mapping(@extschema@.__CDB_FS_add_default_options(config));
role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(server_internal);
row record;
option record;
BEGIN
IF NOT EXISTS (SELECT * FROM pg_extension WHERE extname = 'postgres_fdw') THEN
RAISE EXCEPTION 'postgres_fdw extension is not installed'
USING HINT = 'Please install it with `CREATE EXTENSION postgres_fdw`';
END IF;
-- We only create server and roles if the server didn't exist before
IF NOT EXISTS (SELECT * FROM pg_foreign_server WHERE srvname = server_internal) THEN
BEGIN
EXECUTE FORMAT('CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw', server_internal);
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = role_name) THEN
EXECUTE FORMAT('CREATE ROLE %I NOLOGIN', role_name);
END IF;
EXECUTE FORMAT('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', current_database(), role_name);
-- These grants over `@extschema@` and `@postgisschema@` are necessary for the cases
-- where the schemas aren't accessible to PUBLIC, which is what happens in a CARTO database
EXECUTE FORMAT('GRANT USAGE ON SCHEMA %I TO %I', '@extschema@', role_name);
EXECUTE FORMAT('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA %I TO %I', '@extschema@', role_name);
EXECUTE FORMAT('GRANT USAGE ON SCHEMA %I TO %I', '@postgisschema@', role_name);
EXECUTE FORMAT('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA %I TO %I', '@postgisschema@', role_name);
EXECUTE FORMAT('GRANT SELECT ON ALL TABLES IN SCHEMA %I TO %I', '@postgisschema@', role_name);
EXECUTE FORMAT('GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO %I', role_name);
EXECUTE FORMAT('GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO %I', role_name);
EXECUTE FORMAT('GRANT USAGE ON FOREIGN SERVER %I TO %I', server_internal, role_name);
EXECUTE FORMAT('ALTER SERVER %I OWNER TO %I', server_internal, role_name);
EXECUTE FORMAT ('CREATE USER MAPPING FOR %I SERVER %I', role_name, server_internal);
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Could not create server %: %', server, SQLERRM
USING HINT = 'Please clean the left over objects';
END;
END IF;
-- Add new options
FOR row IN SELECT p.key, p.value from lateral json_each_text(final_config->'server') p
LOOP
IF NOT EXISTS (
WITH a AS (
SELECT split_part(unnest(srvoptions), '=', 1) AS options FROM pg_foreign_server WHERE srvname=server_internal
) SELECT * from a where options = row.key)
THEN
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (ADD %I %L)', server_internal, row.key, row.value);
ELSE
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (SET %I %L)', server_internal, row.key, row.value);
END IF;
END LOOP;
-- Update user mapping settings
FOR option IN SELECT o.key, o.value from lateral json_each_text(final_config->'user_mapping') o
LOOP
IF NOT EXISTS (
WITH a AS (
SELECT split_part(unnest(umoptions), '=', 1) as options from pg_user_mappings WHERE srvname = server_internal AND usename = role_name
) SELECT * from a where options = option.key)
THEN
EXECUTE FORMAT('ALTER USER MAPPING FOR %I SERVER %I OPTIONS (ADD %I %L)', role_name, server_internal, option.key, option.value);
ELSE
EXECUTE FORMAT('ALTER USER MAPPING FOR %I SERVER %I OPTIONS (SET %I %L)', role_name, server_internal, option.key, option.value);
END IF;
END LOOP;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Drops a registered server and all the objects associated with it
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_Unregister(server TEXT)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(server_internal);
BEGIN
SET client_min_messages = ERROR;
BEGIN
EXECUTE FORMAT ('DROP USER MAPPING FOR %I SERVER %I', role_name, server_internal);
EXECUTE FORMAT ('DROP OWNED BY %I', role_name);
EXECUTE FORMAT ('DROP ROLE %I', role_name);
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Not enough permissions to drop the server "%"', server;
END;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List registered servers
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_List_Servers(server TEXT DEFAULT '%')
RETURNS TABLE (
name text,
driver text,
host text,
port text,
dbname text,
readmode text,
username text
)
AS $$
DECLARE
server_name text := concat(@extschema@.__CDB_FS_Name_Pattern(), server);
BEGIN
RETURN QUERY SELECT
-- Name as shown to the user
@extschema@.__CDB_FS_Extract_Server_Name(s.srvname) AS "Name",
-- Which driver are we using (postgres_fdw, odbc_fdw...)
@extschema@.__CDB_FS_server_type(s.srvname)::text AS "Driver",
-- Read options from pg_foreign_server
(SELECT option_value FROM pg_options_to_table(s.srvoptions) WHERE option_name LIKE 'host') AS "Host",
(SELECT option_value FROM pg_options_to_table(s.srvoptions) WHERE option_name LIKE 'port') AS "Port",
(SELECT option_value FROM pg_options_to_table(s.srvoptions) WHERE option_name LIKE 'dbname') AS "DBName",
CASE WHEN (SELECT NOT option_value::boolean FROM pg_options_to_table(s.srvoptions) WHERE option_name LIKE 'updatable') THEN 'read-only' ELSE 'read-write' END AS "ReadMode",
@extschema@.__CDB_FS_get_usermapping_username(s.srvname)::text AS "Username"
FROM pg_foreign_server s
LEFT JOIN pg_user_mappings u
ON u.srvid = s.oid
WHERE s.srvname ILIKE server_name
ORDER BY 1;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL SAFE;
--
-- Grant access to a server
-- In the future we might consider adding the server's view schema to the role search_path
-- to make it easier to access the created views
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_Grant_Access(server TEXT, db_role NAME)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(server_internal);
BEGIN
IF (db_role IS NULL) THEN
RAISE EXCEPTION 'User role "%" cannot be NULL', username;
END IF;
BEGIN
EXECUTE format('GRANT %I TO %I', server_role_name, db_role);
EXCEPTION
WHEN insufficient_privilege THEN
RAISE EXCEPTION 'You do not have rights to grant access on "%"', server;
WHEN OTHERS THEN
RAISE EXCEPTION 'Could not grant access on "%" to "%": %', server, db_role, SQLERRM;
END;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Revoke access to a server
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_Revoke_Access(server TEXT, db_role NAME)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(server_internal);
BEGIN
IF (db_role IS NULL) THEN
RAISE EXCEPTION 'User role "%" cannot be NULL', username;
END IF;
BEGIN
EXECUTE format('REVOKE %I FROM %I', server_role_name, db_role);
EXCEPTION
WHEN insufficient_privilege THEN
RAISE EXCEPTION 'You do not have rights to revoke access on "%"', server;
WHEN OTHERS THEN
RAISE EXCEPTION 'Could not revoke access on "%" to "%": %', server, db_role, SQLERRM;
END;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -1,243 +0,0 @@
--------------------------------------------------------------------------------
-- Private functions
--------------------------------------------------------------------------------
--
-- Import a foreign table if it does not exist
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Import_If_Not_Exists(server_internal name, remote_schema name, remote_table name)
RETURNS void
AS $$
DECLARE
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
BEGIN
IF NOT EXISTS (
SELECT * FROM pg_class
WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = local_schema)
AND relname = remote_table
) THEN
EXECUTE format('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I',
remote_schema, remote_table, server_internal, local_schema);
END IF;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Get the version of a remote PG server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Foreign_Server_Version_PG(server_internal name)
RETURNS text
AS $$
DECLARE
remote_schema name := 'pg_catalog';
remote_table name := 'pg_settings';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
remote_server_version text;
BEGIN
PERFORM @extschema@.__CDB_FS_Import_If_Not_Exists(server_internal, remote_schema, remote_table);
BEGIN
EXECUTE format('
SELECT setting FROM %I.%I WHERE name = ''server_version'';
', local_schema, remote_table) INTO remote_server_version;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Not enough permissions to access the server "%"',
@extschema@.__CDB_FS_Extract_Server_Name(server_internal);
END;
RETURN remote_server_version;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Get the PostGIS extension version of a remote PG server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Foreign_PostGIS_Version_PG(server_internal name)
RETURNS text
AS $$
DECLARE
remote_schema name := 'pg_catalog';
remote_table name := 'pg_extension';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
remote_postgis_version text;
BEGIN
PERFORM @extschema@.__CDB_FS_Import_If_Not_Exists(server_internal, remote_schema, remote_table);
BEGIN
EXECUTE format('
SELECT extversion FROM %I.%I WHERE extname = ''postgis'';
', local_schema, remote_table) INTO remote_postgis_version;
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Not enough permissions to access the server "%"',
@extschema@.__CDB_FS_Extract_Server_Name(server_internal);
END;
RETURN remote_postgis_version;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Get the foreign server options of a remote PG server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Foreign_Server_Options_PG(server_internal name)
RETURNS jsonb
AS $$
-- See https://www.postgresql.org/docs/current/catalog-pg-foreign-server.html
-- See https://www.postgresql.org/docs/current/functions-info.html
SELECT jsonb_object_agg(opt.option_name, opt.option_value) FROM (
SELECT (pg_options_to_table(srvoptions)).*
FROM pg_foreign_server
WHERE srvname = server_internal
) AS opt;
$$
LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
--
-- Get a foreign PG server hostname from the catalog
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Foreign_Server_Host_PG(server_internal name)
RETURNS text
AS $$
SELECT option_value FROM (
SELECT (pg_options_to_table(srvoptions)).*
FROM pg_foreign_server WHERE srvname = server_internal
) AS opt WHERE opt.option_name = 'host';
$$
LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
--
-- Get a foreign PG server port from the catalog
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Foreign_Server_Port_PG(server_internal name)
RETURNS integer
AS $$
SELECT option_value::integer FROM (
SELECT (pg_options_to_table(srvoptions)).*
FROM pg_foreign_server WHERE srvname = server_internal
) AS opt WHERE opt.option_name = 'port';
$$
LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
--
-- Get one measure of network latency in ms to a remote TCP server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_TCP_Foreign_Server_Latency(
server_internal name,
timeout_seconds float DEFAULT 5.0,
n_samples integer DEFAULT 10
)
RETURNS jsonb
AS $$
import socket
import json
import math
from timeit import default_timer as timer
plan = plpy.prepare("SELECT @extschema@.__CDB_FS_Foreign_Server_Host_PG($1) AS host", ['name'])
rv = plpy.execute(plan, [server_internal], 1)
host = rv[0]['host']
plan = plpy.prepare("SELECT @extschema@.__CDB_FS_Foreign_Server_Port_PG($1) AS port", ['name'])
rv = plpy.execute(plan, [server_internal], 1)
port = rv[0]['port'] or 5432
n_errors = 0
samples = []
for i in range(n_samples):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout_seconds)
t_start = timer()
try:
s.connect((host, int(port)))
t_stop = timer()
s.shutdown(socket.SHUT_RD)
except (socket.timeout, OSError, socket.error) as ex:
plpy.warning('could not connect to server %s:%d, %s' % (host, port, str(ex)))
n_errors += 1
finally:
s.close()
t_connect = (t_stop - t_start) * 1000.0
plpy.debug('TCP connection %s:%d time=%.2f ms' % (host, port, t_connect))
samples.append(t_connect)
stats = {
'n_samples': n_samples,
'n_errors': n_errors,
}
n = len(samples)
if n >= 1:
mean = sum(samples) / n
stats.update({
'avg': round(mean, 3),
'min': round(min(samples), 3),
'max': round(max(samples), 3)
})
if n >= 2:
var = sum([ (x - mean)**2 for x in samples ]) / (n-1)
stdev = math.sqrt(var)
stats.update({
'stdev': round(stdev, 3)
})
return json.dumps(stats)
$$
LANGUAGE @@plpythonu@@ VOLATILE PARALLEL UNSAFE;
--
-- Collect and return diagnostics info from a remote PG into a jsonb
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Server_Diagnostics_PG(server_internal name)
RETURNS jsonb
AS $$
DECLARE
remote_server_version text := @extschema@.__CDB_FS_Foreign_Server_Version_PG(server_internal);
remote_postgis_version text := @extschema@.__CDB_FS_Foreign_PostGIS_Version_PG(server_internal);
remote_server_options jsonb := @extschema@.__CDB_FS_Foreign_Server_Options_PG(server_internal);
remote_server_latency_ms jsonb := @extschema@.__CDB_FS_TCP_Foreign_Server_Latency(server_internal);
BEGIN
RETURN jsonb_build_object(
'server_version', remote_server_version,
'postgis_version', remote_postgis_version,
'server_options', remote_server_options,
'server_latency_ms', remote_server_latency_ms
);
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Public functions
--------------------------------------------------------------------------------
--
-- Collect and return diagnostics info from a remote PG into a jsonb
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_Diagnostics(server TEXT)
RETURNS jsonb
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_type name := @extschema@.__CDB_FS_server_type(server_internal);
BEGIN
CASE server_type
WHEN 'postgres_fdw' THEN
RETURN @extschema@.__CDB_FS_Server_Diagnostics_PG(server_internal);
ELSE
RAISE EXCEPTION 'Not implemented server type % for remote server %', server_type, server;
END CASE;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -1,298 +0,0 @@
--------------------------------------------------------------------------------
-- Private functions
--------------------------------------------------------------------------------
--
-- List the schemas of a remote PG server
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_List_Foreign_Schemas_PG(server_internal name)
RETURNS TABLE(remote_schema name)
AS $$
DECLARE
-- Import schemata from the information schema
--
-- "The view schemata contains all schemas in the current database
-- that the current user has access to (by way of being the owner
-- or having some privilege)."
-- See https://www.postgresql.org/docs/11/infoschema-schemata.html
--
-- "The information schema is defined in the SQL standard and can
-- therefore be expected to be portable and remain stable"
-- See https://www.postgresql.org/docs/11/information-schema.html
inf_schema name := 'information_schema';
remote_table name := 'schemata';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, inf_schema);
BEGIN
-- Import the foreign schemata table
IF NOT EXISTS (
SELECT * FROM pg_class
WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = local_schema)
AND relname = remote_table
) THEN
EXECUTE format('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I',
inf_schema, remote_table, server_internal, local_schema);
END IF;
-- Return the result we're interested in. Exclude toast and temp schemas
BEGIN
RETURN QUERY EXECUTE format('
SELECT schema_name::name AS remote_schema FROM %I.%I
WHERE schema_name NOT LIKE %s
ORDER BY remote_schema
', local_schema, remote_table, '''pg_%''');
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Not enough permissions to access the server "%"',
@extschema@.__CDB_FS_Extract_Server_Name(server_internal);
END;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List the names of the tables in a remote PG schema
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_List_Foreign_Tables_PG(server_internal name, remote_schema name)
RETURNS TABLE(remote_table name)
AS $func$
DECLARE
-- Import `tables` from the information schema
--
-- "The view tables contains all tables and views defined in the
-- current database. Only those tables and views are shown that
-- the current user has access to (by way of being the owner or
-- having some privilege)."
-- https://www.postgresql.org/docs/11/infoschema-tables.html
-- Create local target schema if it does not exists
inf_schema name := 'information_schema';
remote_table name := 'tables';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, inf_schema);
BEGIN
-- Import the foreign `tables` if not done
IF NOT EXISTS (
SELECT * FROM pg_class
WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = local_schema)
AND relname = remote_table
) THEN
EXECUTE format('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I',
inf_schema, remote_table, server_internal, local_schema);
END IF;
-- Note: in this context, schema names are not to be quoted
RETURN QUERY EXECUTE format($q$
SELECT table_name::name AS remote_table FROM %I.%I WHERE table_schema = '%s' ORDER BY table_name
$q$, local_schema, remote_table, remote_schema);
END
$func$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List the columns in a remote PG schema
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_List_Foreign_Columns_PG(server_internal name, remote_schema name)
RETURNS TABLE(table_name name, column_name name, column_type text)
AS $func$
DECLARE
-- Import `columns` from the information schema
--
-- "The view columns contains information about all table columns (or view columns)
-- in the database. System columns (oid, etc.) are not included. Only those columns
-- are shown that the current user has access to (by way of being the owner or having some privilege)."
-- https://www.postgresql.org/docs/11/infoschema-columns.html
-- Create local target schema if it does not exists
inf_schema name := 'information_schema';
remote_col_table name := 'columns';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, inf_schema);
BEGIN
-- Import the foreign `columns` if not done
IF NOT EXISTS (
SELECT * FROM pg_class
WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = local_schema)
AND relname = remote_col_table
) THEN
EXECUTE format('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I',
inf_schema, remote_col_table, server_internal, local_schema);
END IF;
-- Note: in this context, schema names are not to be quoted
-- We join with the geometry columns to change the type `USER-DEFINED`
-- by its appropiate geometry and srid
RETURN QUERY EXECUTE format($q$
SELECT
a.table_name::name,
a.column_name::name,
COALESCE(b.column_type, a.data_type)::TEXT as column_type
FROM
%I.%I a
LEFT JOIN
@extschema@.__CDB_FS_List_Foreign_Geometry_Columns_PG('%s', '%s') b
ON a.table_name = b.table_name AND a.column_name = b.column_name
WHERE table_schema = '%s'
ORDER BY a.table_name, a.column_name $q$,
local_schema, remote_col_table,
server_internal, remote_schema,
remote_schema);
END
$func$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List the geometry columns in a remote PG schema
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_List_Foreign_Geometry_Columns_PG(server_internal name, remote_schema name, postgis_schema name DEFAULT 'public')
RETURNS TABLE(table_name name, column_name name, column_type text)
AS $func$
DECLARE
-- Import `geometry_columns` and `geography_columns` from the postgis schema
-- We assume that postgis is installed in the public schema
-- Create local target schema if it does not exists
remote_geometry_view name := 'geometry_columns';
remote_geography_view name := 'geography_columns';
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, postgis_schema);
BEGIN
-- Import the foreign `geometry_columns` and `geography_columns` if not done
IF NOT EXISTS (
SELECT * FROM pg_class
WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = local_schema)
AND relname = remote_geometry_view
) THEN
EXECUTE format('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I, %I) FROM SERVER %I INTO %I',
postgis_schema, remote_geometry_view, remote_geography_view, server_internal, local_schema);
END IF;
BEGIN
-- Note: We return both the type and srid as the type
RETURN QUERY EXECUTE format($q$
SELECT
f_table_name::NAME as table_name,
f_geometry_column::NAME as column_name,
type::TEXT || ',' || srid::TEXT as column_type
FROM
(
SELECT * FROM %I.%I UNION ALL SELECT * FROM %I.%I
) _geo_views
WHERE f_table_schema = '%s'
$q$,
local_schema, remote_geometry_view,
local_schema, remote_geography_view,
remote_schema);
EXCEPTION WHEN OTHERS THEN
RAISE INFO 'Could not find Postgis installation in the remote "%" schema in server "%"',
postgis_schema, @extschema@.__CDB_FS_Extract_Server_Name(server_internal);
RETURN;
END;
END
$func$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Public functions
--------------------------------------------------------------------------------
--
-- List remote schemas in a federated server that the current user has access to.
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_List_Remote_Schemas(server TEXT)
RETURNS TABLE(remote_schema name)
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_type name := @extschema@.__CDB_FS_server_type(server_internal);
BEGIN
CASE server_type
WHEN 'postgres_fdw' THEN
RETURN QUERY SELECT @extschema@.__CDB_FS_List_Foreign_Schemas_PG(server_internal);
ELSE
RAISE EXCEPTION 'Not implemented server type % for remote server %', server_type, server;
END CASE;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List remote tables in a federated server that the current user has access to.
-- For registered tables it returns also the associated configuration
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_List_Remote_Tables(server TEXT, remote_schema TEXT)
RETURNS TABLE(
registered boolean,
remote_table TEXT,
local_qualified_name TEXT,
id_column_name TEXT,
geom_column_name TEXT,
webmercator_column_name TEXT,
columns JSON
)
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_type name := @extschema@.__CDB_FS_server_type(server_internal);
BEGIN
CASE server_type
WHEN 'postgres_fdw' THEN
RETURN QUERY
SELECT
coalesce(registered_tables.registered, false)::boolean as registered,
foreign_tables.remote_table::text as remote_table,
registered_tables.local_qualified_name as local_qualified_name,
registered_tables.id_column_name as id_column_name,
registered_tables.geom_column_name as geom_column_name,
registered_tables.webmercator_column_name as webmercator_column_name,
remote_columns.columns as columns
FROM
@extschema@.__CDB_FS_List_Foreign_Tables_PG(server_internal, remote_schema) foreign_tables
LEFT JOIN
@extschema@.__CDB_FS_List_Registered_Tables(server_internal, remote_schema) registered_tables
ON foreign_tables.remote_table = registered_tables.remote_table
LEFT JOIN
( -- Extract and group columns with their remote table
SELECT table_name,
json_agg(json_build_object('Name', column_name, 'Type', column_type)) as columns
FROM @extschema@.__CDB_FS_List_Foreign_Columns_PG(server_internal, remote_schema)
GROUP BY table_name
) remote_columns
ON foreign_tables.remote_table = remote_columns.table_name
ORDER BY foreign_tables.remote_table;
ELSE
RAISE EXCEPTION 'Not implemented server type % for remote server %', server_type, remote_server;
END CASE;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- List the columns of a remote table in a federated server that the current user has access to.
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Server_List_Remote_Columns(
server TEXT,
remote_schema TEXT,
remote_table TEXT)
RETURNS TABLE(column_n name, column_t text)
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => true);
server_type name := @extschema@.__CDB_FS_server_type(server_internal);
BEGIN
IF remote_table IS NULL THEN
RAISE EXCEPTION 'Remote table name cannot be NULL';
END IF;
CASE server_type
WHEN 'postgres_fdw' THEN
RETURN QUERY
SELECT
column_name,
column_type
FROM @extschema@.__CDB_FS_List_Foreign_Columns_PG(server_internal, remote_schema)
WHERE table_name = remote_table
ORDER BY column_name;
ELSE
RAISE EXCEPTION 'Not implemented server type % for remote server %', server_type, remote_server;
END CASE;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -1,345 +0,0 @@
--------------------------------------------------------------------------------
-- Private functions
--------------------------------------------------------------------------------
--
-- Checks if a column is of integer type
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Column_Is_Integer(input_table REGCLASS, colname NAME)
RETURNS boolean
AS $$
BEGIN
PERFORM atttypid FROM pg_catalog.pg_attribute
WHERE attrelid = input_table
AND attname = colname
AND atttypid IN (SELECT oid FROM pg_type
WHERE typname IN
('smallint', 'integer', 'bigint', 'int2', 'int4', 'int8'));
RETURN FOUND;
END
$$
LANGUAGE PLPGSQL STABLE PARALLEL UNSAFE;
--
-- Checks if a column is of geometry type
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Column_Is_Geometry(input_table REGCLASS, colname NAME)
RETURNS boolean
AS $$
BEGIN
PERFORM atttypid FROM pg_catalog.pg_attribute
WHERE attrelid = input_table
AND attname = colname
AND atttypid = '@postgisschema@.geometry'::regtype;
RETURN FOUND;
END
$$
LANGUAGE PLPGSQL STABLE PARALLEL UNSAFE;
--
-- Returns the name of all the columns from a table
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_GetColumns(input_table REGCLASS)
RETURNS SETOF NAME
AS $$
SELECT
a.attname as "colname"
FROM pg_catalog.pg_attribute a
WHERE
a.attnum > 0
AND NOT a.attisdropped
AND a.attrelid = (
SELECT c.oid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.oid = input_table::oid
)
ORDER BY a.attnum;
$$ LANGUAGE SQL STABLE PARALLEL UNSAFE;
--
-- Returns the id column from a view definition
-- Note: The id is always of one of this forms:
-- SELECT t.cartodb_id,
-- SELECT t.my_id as cartodb_id,
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Get_View_id_column(view_def TEXT)
RETURNS TEXT
AS $$
WITH column_definitions AS
(
SELECT regexp_split_to_array(trim(leading from regexp_split_to_table(view_def, '\n')), ' ') AS col_def
)
SELECT trim(trailing ',' FROM split_part(col_def[2], '.', 2))
FROM column_definitions
WHERE trim(trailing ',' FROM col_def[array_length(col_def, 1)]) IN ('t.cartodb_id', 'cartodb_id')
LIMIT 1;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--
-- Returns the geom column from a view definition
--
-- Note: The the_geom is always of one of this forms:
-- t.the_geom,
-- t.my_geom as the_geom,
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Get_View_geom_column(view_def TEXT)
RETURNS TEXT
AS $$
WITH column_definitions AS
(
SELECT regexp_split_to_array(trim(leading from regexp_split_to_table(view_def, '\n')), ' ') AS col_def
)
SELECT trim(trailing ',' FROM split_part(col_def[1], '.', 2))
FROM column_definitions
WHERE trim(trailing ',' FROM col_def[array_length(col_def, 1)]) IN ('t.the_geom', 'the_geom')
LIMIT 1;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--
-- Returns the webmercatorcolumn from a view definition
-- Note: The the_geom_webmercator is always of one of this forms:
-- t.the_geom_webmercator,
-- t.my_geom as the_geom_webmercator,
-- Or without the trailing comma:
-- t.the_geom_webmercator
-- t.my_geom as the_geom_webmercator
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_Get_View_webmercator_column(view_def TEXT)
RETURNS TEXT
AS $$
WITH column_definitions AS
(
SELECT regexp_split_to_array(trim(leading from regexp_split_to_table(view_def, '\n')), ' ') AS col_def
)
SELECT trim(trailing ',' FROM split_part(col_def[1], '.', 2))
FROM column_definitions
WHERE trim(trailing ',' FROM col_def[array_length(col_def, 1)]) IN ('t.the_geom_webmercator', 'the_geom_webmercator')
LIMIT 1;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
--
-- List all registered tables in a server + schema
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_FS_List_Registered_Tables(
server_internal NAME,
remote_schema TEXT
)
RETURNS TABLE(
registered boolean,
remote_table TEXT,
local_qualified_name TEXT,
id_column_name TEXT,
geom_column_name TEXT,
webmercator_column_name TEXT
)
AS $$
DECLARE
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
BEGIN
RETURN QUERY SELECT
true as registered,
source_table::text as remote_table,
format('%I.%I', dependent_schema, dependent_view)::text as local_qualified_name,
@extschema@.__CDB_FS_Get_View_id_column(view_definition) as id_column_name,
@extschema@.__CDB_FS_Get_View_geom_column(view_definition) as geom_column_name,
@extschema@.__CDB_FS_Get_View_webmercator_column(view_definition) as webmercator_column_name
FROM
(
SELECT DISTINCT
dependent_ns.nspname as dependent_schema,
dependent_view.relname as dependent_view,
source_table.relname as source_table,
pg_get_viewdef(dependent_view.oid) as view_definition
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
WHERE
source_ns.nspname = local_schema
ORDER BY 1,2
) _aux;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Public functions
--------------------------------------------------------------------------------
--
-- Sets up a Federated Table
--
-- Precondition: the federated server has to be set up via
-- CDB_Federated_Server_Register_PG
--
-- Postcondition: it generates a view in the schema of the user that
-- can be used through SQL and Maps API's.
-- If the table was already exported, it will be dropped and re-imported
--
-- The view is placed under the server's view schema (cdb_fs_$(server_name))
--
-- E.g:
-- SELECT cartodb.CDB_SetUp_PG_Federated_Table(
-- 'amazon', -- mandatory, name of the federated server
-- 'my_remote_schema', -- mandatory, schema name
-- 'my_remote_table', -- mandatory, table name
-- 'id', -- mandatory, name of the id column
-- 'geom', -- optional, name of the geom column, preferably in 4326
-- 'webmercator' -- optional, should be in 3857 if present
-- 'local_name' -- optional, name of the local view (uses the remote_name if not declared)
-- );
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Table_Register(
server TEXT,
remote_schema TEXT,
remote_table TEXT,
id_column TEXT,
geom_column TEXT DEFAULT NULL,
webmercator_column TEXT DEFAULT NULL,
local_name NAME DEFAULT NULL
)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => false);
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
role_name name := @extschema@.__CDB_FS_Generate_Server_Role_Name(server_internal);
src_table REGCLASS; -- import_schema.remote_table - import_schema is local_schema
local_view text; -- view_schema.local_name - view_schema is server_internal
rest_of_cols TEXT[];
geom_expression TEXT;
webmercator_expression TEXT;
carto_columns_expression TEXT[];
BEGIN
IF remote_table IS NULL THEN
RAISE EXCEPTION 'Remote table name cannot be NULL';
END IF;
-- Make do with whatever columns are provided
IF webmercator_column IS NULL THEN
webmercator_column := geom_column;
ELSIF geom_column IS NULL THEN
geom_column := webmercator_column;
END IF;
IF local_name IS NULL THEN
local_name := remote_table;
END IF;
-- Import the foreign table
-- Drop the old view / table if there was one
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = local_schema AND table_name = remote_table) THEN
EXECUTE @extschema@.CDB_Federated_Table_Unregister(server, remote_schema, remote_table);
END IF;
BEGIN
EXECUTE FORMAT('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I;',
remote_schema, remote_table, server_internal, local_schema);
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Could not import schema "%" of server "%": %', remote_schema, server, SQLERRM;
END;
BEGIN
src_table := format('%I.%I', local_schema, remote_table);
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Could not import table "%.%" of server "%"', remote_schema, remote_table, server;
END;
-- Check id_column is numeric
IF NOT @extschema@.__CDB_FS_Column_Is_Integer(src_table, id_column) THEN
RAISE EXCEPTION 'non integer id_column "%"', id_column;
END IF;
-- Check if the geom and mercator columns have a geometry type (if provided)
IF geom_column IS NOT NULL AND NOT @extschema@.__CDB_FS_Column_Is_Geometry(src_table, geom_column) THEN
RAISE EXCEPTION 'non geometry column "%"', geom_column;
END IF;
IF webmercator_column IS NOT NULL AND NOT @extschema@.__CDB_FS_Column_Is_Geometry(src_table, webmercator_column) THEN
RAISE EXCEPTION 'non geometry column "%"', webmercator_column;
END IF;
-- Get a list of columns excluding the id, geom and the_geom_webmercator
SELECT ARRAY(
SELECT quote_ident(c) FROM @extschema@.__CDB_FS_GetColumns(src_table) AS c
WHERE c NOT IN (SELECT * FROM (SELECT unnest(ARRAY[id_column, geom_column, webmercator_column, 'cartodb_id', 'the_geom', 'the_geom_webmercator']) col) carto WHERE carto.col IS NOT NULL)
) INTO rest_of_cols;
IF geom_column IS NULL
THEN
geom_expression := 'NULL AS the_geom';
ELSIF @postgisschema@.Find_SRID(local_schema::varchar, remote_table::varchar, geom_column::varchar) = 4326
THEN
geom_expression := format('t.%I AS the_geom', geom_column);
ELSE
-- It needs an ST_Transform to 4326
geom_expression := format('@postgisschema@.ST_Transform(t.%I,4326) AS the_geom', geom_column);
END IF;
IF webmercator_column IS NULL
THEN
webmercator_expression := 'NULL AS the_geom_webmercator';
ELSIF @postgisschema@.Find_SRID(local_schema::varchar, remote_table::varchar, webmercator_column::varchar) = 3857
THEN
webmercator_expression := format('t.%I AS the_geom_webmercator', webmercator_column);
ELSE
-- It needs an ST_Transform to 3857
webmercator_expression := format('@postgisschema@.ST_Transform(t.%I,3857) AS the_geom_webmercator', webmercator_column);
END IF;
-- CARTO columns expressions
carto_columns_expression := ARRAY[
format('t.%1$I AS cartodb_id', id_column),
geom_expression,
webmercator_expression
];
-- Create view schema if it doesn't exist
IF NOT EXISTS (SELECT oid FROM pg_namespace WHERE nspname = server_internal) THEN
EXECUTE 'CREATE SCHEMA ' || quote_ident(server_internal) || ' AUTHORIZATION ' || quote_ident(role_name);
END IF;
-- Create a view with homogeneous CDB fields
BEGIN
EXECUTE format(
'CREATE OR REPLACE VIEW %1$I.%2$I AS
SELECT %3s
FROM %4$s t',
server_internal, local_name,
array_to_string(carto_columns_expression || rest_of_cols, ','),
src_table
);
EXCEPTION WHEN OTHERS THEN
IF EXISTS (SELECT to_regclass(format('%1$I.%2$I', server_internal, local_name))) THEN
RAISE EXCEPTION 'Could not import table "%" as "%.%" already exists', remote_table, server_internal, local_name;
ELSE
RAISE EXCEPTION 'Could not import table "%" as "%": %', remote_table, local_name, SQLERRM;
END IF;
END;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--
-- Unregisters a remote table. Any dependent object will be dropped
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_Federated_Table_Unregister(
server TEXT,
remote_schema TEXT,
remote_table TEXT
)
RETURNS void
AS $$
DECLARE
server_internal name := @extschema@.__CDB_FS_Generate_Server_Name(input_name => server, check_existence => false);
local_schema name := @extschema@.__CDB_FS_Create_Schema(server_internal, remote_schema);
BEGIN
EXECUTE FORMAT ('DROP FOREIGN TABLE %I.%I CASCADE;', local_schema, remote_table);
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -4,7 +4,7 @@
-- All the FDW settings are read from the `cdb_conf.fdws` entry json file.
---------------------------
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDW(fdw_name text, config json)
CREATE OR REPLACE FUNCTION cartodb._CDB_Setup_FDW(fdw_name text, config json)
RETURNS void
AS $$
DECLARE
@ -58,58 +58,58 @@ BEGIN
END IF;
-- Give the organization role usage permisions over the schema
SELECT @extschema@.CDB_Organization_Member_Group_Role_Member_Name() INTO org_role;
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 ''@extschema@'', updatable ''false'')', fdw_name, fdw_name);
EXECUTE FORMAT ('CREATE FOREIGN TABLE %I.cdb_tablemetadata (tabname text, updated_at timestamp with time zone) SERVER %I OPTIONS (table_name ''cdb_tablemetadata_text'', schema_name ''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 VOLATILE PARALLEL UNSAFE;
LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDWS()
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(@extschema@.CDB_Conf_GetConf('fdws')) p LOOP
EXECUTE 'SELECT @extschema@._CDB_Setup_FDW($1, $2)' USING row.key, row.value;
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 VOLATILE PARALLEL UNSAFE;
LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION @extschema@._CDB_Setup_FDW(fdw_name text)
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(@extschema@.CDB_Conf_GetConf('fdws')) p WHERE p.key = fdw_name INTO config;
EXECUTE 'SELECT @extschema@._CDB_Setup_FDW($1, $2)' USING fdw_name, config;
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 PARALLEL UNSAFE;
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION @extschema@.CDB_Add_Remote_Table(source text, table_name text)
CREATE OR REPLACE FUNCTION cartodb.CDB_Add_Remote_Table(source text, table_name text)
RETURNS void AS
$$
BEGIN
PERFORM @extschema@._CDB_Setup_FDW(source);
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 VOLATILE PARALLEL UNSAFE;
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION @extschema@.CDB_Get_Foreign_Updated_At(foreign_table regclass)
CREATE OR REPLACE FUNCTION cartodb.CDB_Get_Foreign_Updated_At(foreign_table regclass)
RETURNS timestamp with time zone AS
$$
DECLARE
@ -125,21 +125,14 @@ BEGIN
-- We assume that the remote cdb_tablemetadata is called cdb_tablemetadata and is on the same schema as the queried table.
SELECT nspname FROM pg_class c, pg_namespace n WHERE c.oid=foreign_table AND c.relnamespace = n.oid INTO fdw_schema_name;
BEGIN
EXECUTE FORMAT('SELECT updated_at FROM %I.cdb_tablemetadata WHERE tabname=%L ORDER BY updated_at DESC LIMIT 1', fdw_schema_name, remote_table_name) INTO time;
EXCEPTION
WHEN undefined_table THEN
-- If you add a GET STACKED DIAGNOSTICS text_var = RETURNED_SQLSTATE
-- you get a code 42P01 which corresponds to undefined_table
RAISE NOTICE 'CDB_Get_Foreign_Updated_At: could not find %.cdb_tablemetadata while checking % updated_at, returning NULL timestamp', fdw_schema_name, foreign_table;
END;
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 VOLATILE PARALLEL UNSAFE;
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION @extschema@._cdb_dbname_of_foreign_table(reloid oid)
CREATE OR REPLACE FUNCTION cartodb._cdb_dbname_of_foreign_table(reloid oid)
RETURNS TEXT AS $$
SELECT option_value FROM pg_options_to_table((
@ -149,25 +142,25 @@ RETURNS TEXT AS $$
WHERE ft.ftrelid = reloid
)) WHERE option_name='dbname';
$$ LANGUAGE SQL VOLATILE PARALLEL UNSAFE;
$$ 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 @extschema@.CDB_QueryTables_Updated_At(query text)
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(@extschema@.CDB_QueryTablesText(query)) schema_table_name
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 @extschema@._cdb_dbname_of_foreign_table(query_tables_oid.reloid)
(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,
@ -179,17 +172,17 @@ AS $$
WHERE c.oid = query_tables_oid.reloid
)
SELECT fqtn.dbname, fqtn.schema_name, fqtn.table_name,
(CASE WHEN relkind = 'f' THEN @extschema@.CDB_Get_Foreign_Updated_At(reloid)
ELSE (SELECT md.updated_at FROM @extschema@.CDB_TableMetadata md WHERE md.tabname = reloid)
(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 VOLATILE PARALLEL UNSAFE;
$$ 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 @extschema@.CDB_Last_Updated_Time(tables text[])
CREATE OR REPLACE FUNCTION cartodb.CDB_Last_Updated_Time(tables text[])
RETURNS timestamptz AS $$
WITH t AS (
SELECT unnest(tables) AS schema_table_name
@ -197,19 +190,10 @@ RETURNS timestamptz AS $$
SELECT (t.schema_table_name)::regclass::oid as reloid FROM t
), t_updated_at AS (
SELECT
(CASE WHEN relkind = 'f' THEN @extschema@.CDB_Get_Foreign_Updated_At(reloid)
ELSE (SELECT md.updated_at FROM @extschema@.CDB_TableMetadata md WHERE md.tabname = reloid)
(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 VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Deprecated
--------------------------------------------------------------------------------
DROP FUNCTION IF EXISTS @extschema@.__CDB_User_FDW_Object_Names(name);
DROP FUNCTION IF EXISTS @extschema@._CDB_SetUp_User_PG_FDW_Server(name, json);
DROP FUNCTION IF EXISTS @extschema@._CDB_Drop_User_PG_FDW_Server(name, boolean);
DROP FUNCTION IF EXISTS @extschema@.CDB_SetUp_User_PG_FDW_Table(name, name, name);
$$ LANGUAGE SQL;

View File

@ -1,159 +0,0 @@
-- Enqueues a job to run Ghost tables linking process for the provided username
CREATE OR REPLACE FUNCTION @extschema@._CDB_LinkGhostTables(username text, db_name text, event_name text)
RETURNS void
AS $$
if not username:
return
if 'json' not in GD:
import json
GD['json'] = json
else:
json = GD['json']
tis_config = plpy.execute("select @extschema@.CDB_Conf_GetConf('invalidation_service');")[0]['cdb_conf_getconf']
if not tis_config:
plpy.warning('Invalidation service configuration not found. Skipping Ghost Tables linking.')
return
tis_config_dict = json.loads(tis_config)
tis_host = tis_config_dict.get('host')
tis_port = tis_config_dict.get('port')
tis_timeout = tis_config_dict.get('timeout', 5)
tis_retry = tis_config_dict.get('retry', 5)
client = GD.get('invalidation', None)
while True:
if not client:
try:
import redis
client = redis.Redis(host=tis_host, port=tis_port, socket_timeout=tis_timeout)
GD['invalidation'] = client
except Exception as err:
# NOTE: no retries on connection error
plpy.warning('Error trying to connect to Invalidation Service to link Ghost Tables: ' + str(err))
break
try:
client.execute_command('DBSCH', db_name, username, event_name)
break
except Exception as err:
client = GD['invalidation'] = None # force reconnect
if not tis_retry:
plpy.warning('Error calling Invalidation Service to link Ghost Tables: ' + str(err))
break
tis_retry -= 1 # try reconnecting
$$ LANGUAGE '@@plpythonu@@' VOLATILE PARALLEL UNSAFE;
-- Enqueues a job to run Ghost tables linking process for the current user
CREATE OR REPLACE FUNCTION @extschema@.CDB_LinkGhostTables(event_name text DEFAULT 'USER')
RETURNS void
AS $$
DECLARE
username TEXT;
db_name TEXT;
BEGIN
EXECUTE 'SELECT @extschema@.CDB_Username();' INTO username;
EXECUTE 'SELECT current_database();' INTO db_name;
PERFORM @extschema@._CDB_LinkGhostTables(username, db_name, event_name);
RAISE INFO '_CDB_LinkGhostTables() called with username=%, event_name=%', username, event_name;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Trigger function to call CDB_LinkGhostTables()
CREATE OR REPLACE FUNCTION @extschema@._CDB_LinkGhostTablesTrigger()
RETURNS trigger
AS $$
DECLARE
ddl_tag TEXT;
BEGIN
EXECUTE 'DELETE FROM @extschema@.cdb_ddl_execution WHERE txid = txid_current() RETURNING tag;' INTO ddl_tag;
PERFORM @extschema@.CDB_LinkGhostTables(ddl_tag);
RETURN NULL;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Event trigger to save the current transaction in @extschema@.cdb_ddl_execution
CREATE OR REPLACE FUNCTION @extschema@.CDB_SaveDDLTransaction()
RETURNS event_trigger
AS $$
BEGIN
INSERT INTO @extschema@.cdb_ddl_execution VALUES (txid_current(), tg_tag) ON CONFLICT ON CONSTRAINT cdb_ddl_execution_pkey DO NOTHING;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Creates the trigger on DDL events to link ghost tables
CREATE OR REPLACE FUNCTION @extschema@.CDB_EnableGhostTablesTrigger()
RETURNS void
AS $$
BEGIN
DROP EVENT TRIGGER IF EXISTS link_ghost_tables;
DROP TRIGGER IF EXISTS check_ddl_update ON @extschema@.cdb_ddl_execution;
-- Table to store the transaction id from DDL events to avoid multiple executions
CREATE TABLE IF NOT EXISTS @extschema@.cdb_ddl_execution(txid bigint PRIMARY KEY, tag text);
CREATE CONSTRAINT TRIGGER check_ddl_update
AFTER INSERT ON @extschema@.cdb_ddl_execution
INITIALLY DEFERRED
FOR EACH ROW
EXECUTE PROCEDURE @extschema@._CDB_LinkGhostTablesTrigger();
CREATE EVENT TRIGGER link_ghost_tables
ON ddl_command_end
WHEN TAG IN ('CREATE TABLE',
'SELECT INTO',
'DROP TABLE',
'ALTER TABLE',
'CREATE TRIGGER',
'DROP TRIGGER',
'ALTER TRIGGER',
'CREATE VIEW',
'DROP VIEW',
'ALTER VIEW',
'CREATE FOREIGN TABLE',
'ALTER FOREIGN TABLE',
'DROP FOREIGN TABLE',
'ALTER MATERIALIZED VIEW',
'CREATE MATERIALIZED VIEW',
'DROP MATERIALIZED VIEW',
'IMPORT FOREIGN SCHEMA',
'DROP EXTENSION',
'DROP SCHEMA',
'DROP SERVER',
'DROP TYPE')
EXECUTE PROCEDURE @extschema@.CDB_SaveDDLTransaction();
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- Drops the trigger on DDL events to link ghost tables
CREATE OR REPLACE FUNCTION @extschema@.CDB_DisableGhostTablesTrigger()
RETURNS void
AS $$
BEGIN
DROP EVENT TRIGGER IF EXISTS link_ghost_tables;
DROP TRIGGER IF EXISTS check_ddl_update ON @extschema@.cdb_ddl_execution;
DROP TABLE IF EXISTS @extschema@.cdb_ddl_execution;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;

View File

@ -1,26 +1,26 @@
-- Great circle point-to-point routes, based on:
-- http://blog.cartodb.com/jets-and-datelines/
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_GreatCircle(start_point @postgisschema@.geometry, end_point @postgisschema@.geometry, max_segment_length NUMERIC DEFAULT 100000)
RETURNS @postgisschema@.geometry AS $$
CREATE OR REPLACE FUNCTION CDB_GreatCircle(start_point geometry, end_point geometry, max_segment_length NUMERIC DEFAULT 100000)
RETURNS geometry AS $$
DECLARE
line @postgisschema@.geometry;
line geometry;
BEGIN
line = @postgisschema@.ST_Segmentize(
@postgisschema@.ST_Makeline(
line = ST_Segmentize(
ST_Makeline(
start_point,
end_point
)::geography,
max_segment_length
)::geometry;
IF @postgisschema@.ST_XMax(line) - @postgisschema@.ST_XMin(line) > 180 THEN
line = @postgisschema@.ST_Difference(
@postgisschema@.ST_ShiftLongitude(line),
@postgisschema@.ST_Buffer(@postgisschema@.ST_GeomFromText('LINESTRING(180 90, 180 -90)', 4326), 0.00001)
IF ST_XMax(line) - ST_XMin(line) > 180 THEN
line = ST_Difference(
ST_Shift_Longitude(line),
ST_Buffer(ST_GeomFromText('LINESTRING(180 90, 180 -90)', 4326), 0.00001)
);
END IF;
RETURN line;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL SAFE;
LANGUAGE 'plpgsql';

View File

@ -6,16 +6,16 @@
-- Creates a new group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_CreateGroup(group_name text)
FUNCTION cartodb.CDB_Group_CreateGroup(group_name text)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
EXECUTE format('CREATE ROLE %I NOLOGIN;', group_role);
PERFORM @extschema@._CDB_Group_CreateGroup_API(group_name, group_role);
PERFORM cartodb._CDB_Group_CreateGroup_API(group_name, group_role);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Drops group and everything that role owns
-- TODO: LIMITATION: in order to drop a role all its owned objects must be dropped before.
@ -23,74 +23,74 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
-- Not even the role creator can drop the role and the objects it owns.
-- All group owned objects by the group are permissions.
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_DropGroup(group_name text)
FUNCTION cartodb.CDB_Group_DropGroup(group_name text)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
EXECUTE format('DROP OWNED BY %I', group_role);
EXECUTE format('DROP ROLE IF EXISTS %I', group_role);
PERFORM @extschema@._CDB_Group_DropGroup_API(group_name);
PERFORM cartodb._CDB_Group_DropGroup_API(group_name);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Renames a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_RenameGroup(old_group_name text, new_group_name text)
FUNCTION cartodb.CDB_Group_RenameGroup(old_group_name text, new_group_name text)
RETURNS VOID AS $$
DECLARE
old_group_role TEXT;
new_group_role TEXT;
BEGIN
old_group_role = @extschema@._CDB_Group_GroupRole(old_group_name);
new_group_role = @extschema@._CDB_Group_GroupRole(new_group_name);
old_group_role = cartodb._CDB_Group_GroupRole(old_group_name);
new_group_role = cartodb._CDB_Group_GroupRole(new_group_name);
EXECUTE format('ALTER ROLE %I RENAME TO %I', old_group_role, new_group_role);
PERFORM @extschema@._CDB_Group_RenameGroup_API(old_group_name, new_group_name, new_group_role);
PERFORM cartodb._CDB_Group_RenameGroup_API(old_group_name, new_group_name, new_group_role);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Adds users to a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_AddUsers(group_name text, usernames text[])
FUNCTION cartodb.CDB_Group_AddUsers(group_name text, usernames text[])
RETURNS VOID AS $$
DECLARE
group_role TEXT;
user_role TEXT;
username TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
foreach username in array usernames
loop
user_role := @extschema@._CDB_User_RoleFromUsername(username);
user_role := cartodb._CDB_User_RoleFromUsername(username);
IF(group_role IS NULL OR user_role IS NULL)
THEN
RAISE EXCEPTION 'Group role (%) and user role (%) must be already existing', group_role, user_role;
END IF;
EXECUTE format('GRANT %I TO %I', group_role, user_role);
end loop;
PERFORM @extschema@._CDB_Group_AddUsers_API(group_name, usernames);
PERFORM cartodb._CDB_Group_AddUsers_API(group_name, usernames);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Removes users from a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_RemoveUsers(group_name text, usernames text[])
FUNCTION cartodb.CDB_Group_RemoveUsers(group_name text, usernames text[])
RETURNS VOID AS $$
DECLARE
group_role TEXT;
user_role TEXT;
username TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
foreach username in array usernames
loop
user_role := @extschema@._CDB_User_RoleFromUsername(username);
user_role := cartodb._CDB_User_RoleFromUsername(username);
EXECUTE format('REVOKE %I FROM %I', group_role, user_role);
end loop;
PERFORM @extschema@._CDB_Group_RemoveUsers_API(group_name, usernames);
PERFORM cartodb._CDB_Group_RemoveUsers_API(group_name, usernames);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
----------------------------------
-- TABLE MANAGEMENT FUNCTIONS
@ -100,67 +100,67 @@ $$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
-- Grants table read permission to a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_Table_GrantRead(group_name text, username text, table_name text)
FUNCTION cartodb.CDB_Group_Table_GrantRead(group_name text, username text, table_name text)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
PERFORM @extschema@._CDB_Group_Table_GrantRead(group_name, username, table_name, true);
PERFORM cartodb._CDB_Group_Table_GrantRead(group_name, username, table_name, true);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_Table_GrantRead(group_name text, username text, table_name text, sync boolean)
FUNCTION cartodb._CDB_Group_Table_GrantRead(group_name text, username text, table_name text, sync boolean)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', username, group_role);
EXECUTE format('GRANT SELECT ON TABLE %I.%I TO %I', username, table_name, group_role );
IF(sync) THEN
PERFORM @extschema@._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'r');
PERFORM cartodb._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'r');
END IF;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Grants table write permission to a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text)
FUNCTION cartodb.CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
PERFORM @extschema@._CDB_Group_Table_GrantReadWrite(group_name, username, table_name, true);
PERFORM cartodb._CDB_Group_Table_GrantReadWrite(group_name, username, table_name, true);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text, sync boolean)
FUNCTION cartodb._CDB_Group_Table_GrantReadWrite(group_name text, username text, table_name text, sync boolean)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', username, group_role);
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I', username, table_name, group_role);
PERFORM @extschema@._CDB_Group_TableSequences_Permission(group_name, username, table_name, true);
PERFORM cartodb._CDB_Group_TableSequences_Permission(group_name, username, table_name, true);
IF(sync) THEN
PERFORM @extschema@._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'w');
PERFORM cartodb._CDB_Group_Table_GrantPermission_API(group_name, username, table_name, 'w');
END IF;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Granting and revoking permissions on sequences
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_TableSequences_Permission(group_name text, username text, table_name text, do_grant bool)
FUNCTION cartodb._CDB_Group_TableSequences_Permission(group_name text, username text, table_name text, do_grant bool)
RETURNS VOID AS $$
DECLARE
column_name TEXT;
sequence_name TEXT;
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
FOR column_name IN EXECUTE 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = current_database() AND TABLE_SCHEMA = $1 AND TABLE_NAME = $2 AND COLUMN_DEFAULT LIKE ''nextval%''' USING username, table_name
LOOP
EXECUTE format('SELECT PG_GET_SERIAL_SEQUENCE(''%I.%I'', ''%I'')', username, table_name, column_name) INTO sequence_name;
@ -175,47 +175,47 @@ BEGIN
END LOOP;
RETURN;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-- Revokes all permissions on a table from a group
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Group_Table_RevokeAll(group_name text, username text, table_name text)
FUNCTION cartodb.CDB_Group_Table_RevokeAll(group_name text, username text, table_name text)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
PERFORM @extschema@._CDB_Group_Table_RevokeAll(group_name, username, table_name, true);
PERFORM cartodb._CDB_Group_Table_RevokeAll(group_name, username, table_name, true);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_Table_RevokeAll(group_name text, username text, table_name text, sync boolean)
FUNCTION cartodb._CDB_Group_Table_RevokeAll(group_name text, username text, table_name text, sync boolean)
RETURNS VOID AS $$
DECLARE
group_role TEXT;
BEGIN
group_role := @extschema@._CDB_Group_GroupRole(group_name);
group_role := cartodb._CDB_Group_GroupRole(group_name);
EXECUTE format('REVOKE ALL ON TABLE %I.%I FROM %I', username, table_name, group_role);
PERFORM @extschema@._CDB_Group_TableSequences_Permission(group_name, username, table_name, false);
PERFORM cartodb._CDB_Group_TableSequences_Permission(group_name, username, table_name, false);
IF(sync) THEN
PERFORM @extschema@._CDB_Group_Table_RevokeAllPermission_API(group_name, username, table_name);
PERFORM cartodb._CDB_Group_Table_RevokeAllPermission_API(group_name, username, table_name);
END IF;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-----------------------
-- Helper functions
-----------------------
-- Given a group name returns a role. group_name must be a valid PostgreSQL idenfifier. See http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_GroupRole(group_name text)
FUNCTION cartodb._CDB_Group_GroupRole(group_name text)
RETURNS TEXT AS $$
DECLARE
group_role TEXT;
prefix TEXT;
max_length constant INTEGER := 63;
BEGIN
prefix = format('%s_g_', @extschema@._CDB_Group_ShortDatabaseName());
prefix = format('%s_g_', cartodb._CDB_Group_ShortDatabaseName());
group_role := format('%s%s', prefix, group_name);
IF LENGTH(group_role) > max_length
THEN
@ -223,11 +223,11 @@ BEGIN
END IF;
RETURN group_role;
END
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
$$ LANGUAGE PLPGSQL;
-- Returns the first owner of the schema matching username. Organization user schemas must have one only owner.
CREATE OR REPLACE
FUNCTION @extschema@._CDB_User_RoleFromUsername(username text)
FUNCTION cartodb._CDB_User_RoleFromUsername(username text)
RETURNS TEXT AS $$
DECLARE
user_role TEXT;
@ -237,11 +237,11 @@ BEGIN
SELECT pg_get_userbyid(nspowner) FROM pg_namespace WHERE nspname = username INTO user_role;
RETURN user_role;
END
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
$$ LANGUAGE PLPGSQL;
-- Database names are too long, we need a shorter version for composing role names
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_ShortDatabaseName()
FUNCTION cartodb._CDB_Group_ShortDatabaseName()
RETURNS TEXT AS $$
DECLARE
short_database_name TEXT;
@ -249,4 +249,4 @@ BEGIN
SELECT md5(current_database()) INTO short_database_name;
RETURN short_database_name;
END
$$ LANGUAGE PLPGSQL STABLE PARALLEL SAFE;
$$ LANGUAGE PLPGSQL;

View File

@ -2,175 +2,130 @@
-- GROUP METADATA API FUNCTIONS
--
-- Meant to be used by CDB_Group_* functions to sync data with the editor.
-- Requires configuration parameter. Example: SELECT @extschema@.CDB_Conf_SetConf('groups_api', '{ "host": "127.0.0.1", "port": 3000, "timeout": 10, "username": "extension", "password": "elephant" }');
-- Requires configuration parameter. Example: SELECT cartodb.CDB_Conf_SetConf('groups_api', '{ "host": "127.0.0.1", "port": 3000, "timeout": 10, "username": "extension", "password": "elephant" }');
----------------------------------
DROP FUNCTION IF EXISTS @extschema@.CDB_Group_AddMember(group_name text, username text);
DROP FUNCTION IF EXISTS @extschema@.CDB_Group_RemoveMember(group_name text, username text);
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_AddMember_API(group_name text, username text);
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_RemoveMember_API(group_name text, username text);
-- TODO: delete this development cleanup before final merge
DROP FUNCTION IF EXISTS cartodb.CDB_Group_AddMember(group_name text, username text);
DROP FUNCTION IF EXISTS cartodb.CDB_Group_RemoveMember(group_name text, username text);
DROP FUNCTION IF EXISTS cartodb._CDB_Group_AddMember_API(group_name text, username text);
DROP FUNCTION IF EXISTS cartodb._CDB_Group_RemoveMember_API(group_name text, username text);
-- Sends the create group request
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_CreateGroup_API(group_name text, group_role text)
FUNCTION cartodb._CDB_Group_CreateGroup_API(group_name text, group_role text)
RETURNS VOID AS
$$
import string
url = '/api/v1/databases/{0}/groups'
body = '{ "name": "%s", "database_role": "%s" }' % (group_name, group_role)
query = "select @extschema@._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
query = "select cartodb._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_DropGroup_API(group_name text)
FUNCTION cartodb._CDB_Group_DropGroup_API(group_name text)
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s' % (pathname2url(group_name))
url = '/api/v1/databases/{0}/groups/%s' % (urllib.pathname2url(group_name))
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '', '{204, 404}') as response_status" % url
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '', '{204, 404}') as response_status" % url
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_RenameGroup_API(old_group_name text, new_group_name text, new_group_role text)
FUNCTION cartodb._CDB_Group_RenameGroup_API(old_group_name text, new_group_name text, new_group_role text)
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s' % (pathname2url(old_group_name))
url = '/api/v1/databases/{0}/groups/%s' % (urllib.pathname2url(old_group_name))
body = '{ "name": "%s", "database_role": "%s" }' % (new_group_name, new_group_role)
query = "select @extschema@._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
query = "select cartodb._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_AddUsers_API(group_name text, usernames text[])
FUNCTION cartodb._CDB_Group_AddUsers_API(group_name text, usernames text[])
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s/users' % (pathname2url(group_name))
url = '/api/v1/databases/{0}/groups/%s/users' % (urllib.pathname2url(group_name))
body = "{ \"users\": [\"%s\"] }" % "\",\"".join(usernames)
query = "select @extschema@._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
query = "select cartodb._CDB_Group_API_Request('POST', '%s', '%s', '{200, 409}') as response_status" % (url, body)
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_RemoveUsers_API(group_name text, usernames text[])
FUNCTION cartodb._CDB_Group_RemoveUsers_API(group_name text, usernames text[])
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s/users' % (pathname2url(group_name))
url = '/api/v1/databases/{0}/groups/%s/users' % (urllib.pathname2url(group_name))
body = "{ \"users\": [\"%s\"] }" % "\",\"".join(usernames)
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '%s', '{200, 404}') as response_status" % (url, body)
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '%s', '{200, 404}') as response_status" % (url, body)
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
DO LANGUAGE 'plpgsql' $$
BEGIN
-- Needed for dropping type
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
DROP TYPE IF EXISTS _CDB_Group_API_Params;
END
$$;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_Table_GrantPermission_API(group_name text, username text, table_name text, access text)
FUNCTION cartodb._CDB_Group_Table_GrantPermission_API(group_name text, username text, table_name text, access text)
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (pathname2url(group_name), username, table_name)
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (urllib.pathname2url(group_name), username, table_name)
body = '{ "access": "%s" }' % access
query = "select @extschema@._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
query = "select cartodb._CDB_Group_API_Request('PUT', '%s', '%s', '{200, 409}') as response_status" % (url, body)
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
DO LANGUAGE 'plpgsql' $$
BEGIN
-- Needed for dropping type
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
DROP TYPE IF EXISTS _CDB_Group_API_Params;
END
$$;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_Table_RevokeAllPermission_API(group_name text, username text, table_name text)
FUNCTION cartodb._CDB_Group_Table_RevokeAllPermission_API(group_name text, username text, table_name text)
RETURNS VOID AS
$$
import string
try:
from urllib import pathname2url
except:
from urllib.request import pathname2url
import urllib
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (pathname2url(group_name), username, table_name)
query = "select @extschema@._CDB_Group_API_Request('DELETE', '%s', '', '{200, 404}') as response_status" % url
url = '/api/v1/databases/{0}/groups/%s/permission/%s/tables/%s' % (urllib.pathname2url(group_name), username, table_name)
query = "select cartodb._CDB_Group_API_Request('DELETE', '%s', '', '{200, 404}') as response_status" % url
plpy.execute(query)
$$ LANGUAGE '@@plpythonu@@'
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$ LANGUAGE 'plpythonu' VOLATILE SECURITY DEFINER;
DO LANGUAGE 'plpgsql' $$
BEGIN
-- Needed for dropping type
DROP FUNCTION IF EXISTS @extschema@._CDB_Group_API_Conf();
DROP TYPE IF EXISTS @extschema@._CDB_Group_API_Params;
DROP FUNCTION IF EXISTS cartodb._CDB_Group_API_Conf();
DROP TYPE IF EXISTS _CDB_Group_API_Params;
END
$$;
CREATE TYPE @extschema@._CDB_Group_API_Params AS (
CREATE TYPE _CDB_Group_API_Params AS (
host text,
port int,
timeout int,
@ -180,49 +135,35 @@ CREATE TYPE @extschema@._CDB_Group_API_Params AS (
-- This must be explicitally extracted because "composite types are currently not supported".
-- See http://www.postgresql.org/docs/9.3/static/plpython-database.html.
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_API_Conf()
RETURNS @extschema@._CDB_Group_API_Params AS
FUNCTION cartodb._CDB_Group_API_Conf()
RETURNS _CDB_Group_API_Params AS
$$
conf = plpy.execute("SELECT @extschema@.CDB_Conf_GetConf('groups_api') conf")[0]['conf']
conf = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('groups_api') conf")[0]['conf']
if conf is None:
return None
else:
import json
params = json.loads(conf)
auth = 'Basic %s' % plpy.execute("SELECT @extschema@._CDB_Group_API_Auth('%s', '%s') as auth" % (params['username'], params['password']))[0]['auth']
auth = 'Basic %s' % plpy.execute("SELECT cartodb._CDB_Group_API_Auth('%s', '%s') as auth" % (params['username'], params['password']))[0]['auth']
return { "host": params['host'], "port": params['port'], 'timeout': params['timeout'], 'auth': auth }
$$ LANGUAGE '@@plpythonu@@' VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE 'plpythonu' VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_API_Auth(username text, password text)
FUNCTION cartodb._CDB_Group_API_Auth(username text, password text)
RETURNS TEXT AS
$$
import sys
import base64
data_to_encode = '%s:%s' % (username, password)
if sys.version_info[0] < 3:
data_encoded = base64.encodestring(data_to_encode)
else:
data_encoded = base64.b64encode(data_to_encode.encode()).decode()
data_encoded = data_encoded.replace('\n', '')
return data_encoded
$$ LANGUAGE '@@plpythonu@@' VOLATILE PARALLEL UNSAFE;
return base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
$$ LANGUAGE 'plpythonu' VOLATILE;
-- url must contain a '%s' placeholder that will be replaced by current_database, for security reasons.
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Group_API_Request(method text, url text, body text, valid_return_codes int[])
FUNCTION cartodb._CDB_Group_API_Request(method text, url text, body text, valid_return_codes int[])
RETURNS int AS
$$
python_v2 = True
try:
import httplib as client
except:
from http import client
python_v2 = False
import httplib
params = plpy.execute("select c.host, c.port, c.timeout, c.auth from @extschema@._CDB_Group_API_Conf() c;")[0]
params = plpy.execute("select c.host, c.port, c.timeout, c.auth from cartodb._CDB_Group_API_Conf() c;")[0]
if params['host'] is None:
return None
@ -233,25 +174,22 @@ $$
last_err = None
while retry > 0:
try:
if python_v2:
conn = SD['groups_api_client'] = client.HTTPConnection(params['host'], params['port'], False, params['timeout'])
else:
conn = SD['groups_api_client'] = client.HTTPConnection(params['host'], port=params['port'], timeout=params['timeout'])
client = SD['groups_api_client'] = httplib.HTTPConnection(params['host'], params['port'], False, params['timeout'])
database_name = plpy.execute("select current_database();")[0]['current_database']
conn.request(method, url.format(database_name), body, headers)
response = conn.getresponse()
client.request(method, url.format(database_name), body, headers)
response = client.getresponse()
assert response.status in valid_return_codes
return response.status
except Exception as err:
retry -= 1
last_err = err
plpy.warning('Retrying after: ' + str(err))
conn = SD['groups_api_client'] = None
client = SD['groups_api_client'] = None
if last_err is not None:
plpy.error('Fatal Group API error: ' + str(last_err))
raise last_err
return None
$$ LANGUAGE '@@plpythonu@@' VOLATILE PARALLEL UNSAFE;
revoke all on function @extschema@._CDB_Group_API_Request(text, text, text, int[]) from public;
$$ LANGUAGE 'plpythonu' VOLATILE;
revoke all on function cartodb._CDB_Group_API_Request(text, text, text, int[]) from public;

View File

@ -8,7 +8,7 @@
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_HeadsTailsBins ( in_array NUMERIC[], breaks INT) RETURNS NUMERIC[] as $$
CREATE OR REPLACE FUNCTION CDB_HeadsTailsBins ( in_array NUMERIC[], breaks INT) RETURNS NUMERIC[] as $$
DECLARE
element_count INT4;
arr_mean numeric;
@ -43,4 +43,4 @@ BEGIN
END LOOP;
RETURN reply;
END;
$$ language plpgsql IMMUTABLE PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;

View File

@ -1,11 +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.
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Identifier(prefix TEXT, relname TEXT, suffix TEXT, schema TEXT DEFAULT NULL)
CREATE OR REPLACE FUNCTION cartodb._CDB_Unique_Identifier(prefix TEXT, relname TEXT, suffix TEXT, schema TEXT DEFAULT NULL)
RETURNS TEXT
AS $$
DECLARE
@ -19,15 +15,15 @@ DECLARE
i INTEGER;
BEGIN
-- Accounts for the XXXX incremental suffix in case the identifier is taken
usedspace := 4;
-- Accounts for the _XX incremental suffix in case the identifier is taken
usedspace := 3;
usedspace := usedspace + coalesce(octet_length(prefix), 0);
usedspace := usedspace + coalesce(octet_length(suffix), 0);
candrelname := @extschema@._CDB_Octet_Truncate(relname, maxlen - usedspace);
candrelname := _CDB_Octet_Truncate(relname, maxlen - usedspace);
IF candrelname = '' THEN
PERFORM @extschema@._CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Identifier');
PERFORM _CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Identifier');
END IF;
ident := coalesce(prefix, '') || candrelname || coalesce(suffix, '');
@ -35,7 +31,7 @@ BEGIN
i := 0;
origident := ident;
WHILE i < 10000 LOOP
WHILE i < 100 LOOP
IF schema IS NOT NULL THEN
SELECT c.relname, n.nspname
INTO rec
@ -55,18 +51,18 @@ BEGIN
RETURN ident;
END IF;
ident := origident || i;
ident := origident || '_' || i;
i := i + 1;
END LOOP;
PERFORM @extschema@._CDB_Error('looping too far', '_CDB_Unique_Identifier');
PERFORM _CDB_Error('looping too far', '_CDB_Unique_Identifier');
END;
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE 'plpgsql';
-- UTF8 safe and length aware. Find a unique identifier for a column with a given prefix
-- and/or suffix based on colname and within a relation specified via reloid.
CREATE OR REPLACE FUNCTION @extschema@._CDB_Unique_Column_Identifier(prefix TEXT, colname TEXT, suffix TEXT, reloid REGCLASS)
CREATE OR REPLACE FUNCTION cartodb._CDB_Unique_Column_Identifier(prefix TEXT, colname TEXT, suffix TEXT, reloid REGCLASS)
RETURNS TEXT
AS $$
DECLARE
@ -80,15 +76,15 @@ DECLARE
i INTEGER;
BEGIN
-- Accounts for the XXXX incremental suffix in case the identifier is taken
usedspace := 4;
-- Accounts for the _XX incremental suffix in case the identifier is taken
usedspace := 3;
usedspace := usedspace + coalesce(octet_length(prefix), 0);
usedspace := usedspace + coalesce(octet_length(suffix), 0);
candcolname := @extschema@._CDB_Octet_Truncate(colname, maxlen - usedspace);
candcolname := _CDB_Octet_Truncate(colname, maxlen - usedspace);
IF candcolname = '' THEN
PERFORM @extschema@._CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Column_Identifier');
PERFORM _CDB_Error('prefixes are to long to generate a valid identifier', '_CDB_Unique_Column_Identifier');
END IF;
ident := coalesce(prefix, '') || candcolname || coalesce(suffix, '');
@ -96,7 +92,7 @@ BEGIN
i := 0;
origident := ident;
WHILE i < 10000 LOOP
WHILE i < 100 LOOP
SELECT a.attname
INTO rec
FROM pg_class c
@ -110,18 +106,18 @@ BEGIN
RETURN ident;
END IF;
ident := origident || i;
ident := origident || '_' || i;
i := i + 1;
END LOOP;
PERFORM @extschema@._CDB_Error('looping too far', '_CDB_Unique_Column_Identifier');
PERFORM _CDB_Error('looping too far', '_CDB_Unique_Column_Identifier');
END;
$$ LANGUAGE 'plpgsql' VOLATILE PARALLEL SAFE;
$$ LANGUAGE 'plpgsql';
-- Truncates a given string to a max_octets octets taking care
-- not to leave characters in half. UTF8 safe.
CREATE OR REPLACE FUNCTION @extschema@._CDB_Octet_Truncate(string TEXT, max_octets INTEGER)
CREATE OR REPLACE FUNCTION cartodb._CDB_Octet_Truncate(string TEXT, max_octets INTEGER)
RETURNS TEXT
AS $$
DECLARE
@ -157,21 +153,4 @@ BEGIN
RETURN left(string, (i - 1));
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE PARALLEL SAFE;
-- Checks if a given text representing a qualified or unqualified table name (relation)
-- actually exists in the database. It is meant to be used as a guard for other function/queries.
CREATE OR REPLACE FUNCTION @extschema@._CDB_Table_Exists(table_name_with_optional_schema TEXT)
RETURNS bool
AS $$
DECLARE
table_exists bool := false;
BEGIN
table_exists := EXISTS(SELECT * FROM pg_class WHERE table_name_with_optional_schema::regclass::oid = oid AND relkind = 'r');
RETURN table_exists;
EXCEPTION
WHEN invalid_schema_name OR undefined_table THEN
RETURN false;
END;
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE 'plpgsql';

View File

@ -1,18 +1,14 @@
-- Return an Hexagon with given center and side (or maximal radius)
CREATE OR REPLACE FUNCTION @extschema@.CDB_MakeHexagon(center GEOMETRY, radius FLOAT8)
CREATE OR REPLACE FUNCTION CDB_MakeHexagon(center GEOMETRY, radius FLOAT8)
RETURNS GEOMETRY
AS $$
SELECT @postgisschema@.ST_MakePolygon(@postgisschema@.ST_MakeLine(geom))
SELECT ST_MakePolygon(ST_MakeLine(geom))
FROM
(
SELECT (@postgisschema@.ST_DumpPoints(@postgisschema@.ST_ExteriorRing(@postgisschema@.ST_Buffer($1, $2, 3)))).*
SELECT (ST_DumpPoints(ST_ExteriorRing(ST_Buffer($1, $2, 3)))).*
) as points
WHERE path[1] % 2 != 0
$$ LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE;
-- In older versions of the extension, CDB_HexagonGrid had a different signature
DROP FUNCTION IF EXISTS @extschema@.CDB_HexagonGrid(GEOMETRY, FLOAT8, GEOMETRY);
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
--
-- Fill given extent with an hexagonal coverage
@ -29,13 +25,10 @@ DROP FUNCTION IF EXISTS @extschema@.CDB_HexagonGrid(GEOMETRY, FLOAT8, GEOMETRY);
-- If omitted the origin will be 0,0.
-- The parameter is checked for having the same SRID
-- as the extent.
--
--
-- @param maxcells Optional maximum number of grid cells to generate;
-- if the grid requires more cells to cover the extent
-- and exception will occur.
----
-- DROP FUNCTION IF EXISTS CDB_HexagonGrid(ext GEOMETRY, side FLOAT8);
CREATE OR REPLACE FUNCTION @extschema@.CDB_HexagonGrid(ext GEOMETRY, side FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
CREATE OR REPLACE FUNCTION CDB_HexagonGrid(ext GEOMETRY, side FLOAT8, origin GEOMETRY DEFAULT NULL)
RETURNS SETOF GEOMETRY
AS $$
DECLARE
@ -80,11 +73,11 @@ BEGIN
yoff := 0;
IF origin IS NOT NULL THEN
IF @postgisschema@.ST_SRID(origin) != srid THEN
IF ST_SRID(origin) != srid THEN
RAISE EXCEPTION 'SRID mismatch between extent (%) and origin (%)', srid, ST_SRID(origin);
END IF;
xoff := @postgisschema@.ST_X(origin);
yoff := @postgisschema@.ST_Y(origin);
xoff := ST_X(origin);
yoff := ST_Y(origin);
END IF;
RAISE DEBUG 'X offset: %', xoff;
@ -96,53 +89,47 @@ BEGIN
RAISE DEBUG 'Y grid size: %', ygrd;
-- Tweak horizontal start on hstep*2 grid from origin
hskip := ceil((@postgisschema@.ST_XMin(ext)-xoff)/hstep);
hskip := ceil((ST_XMin(ext)-xoff)/hstep);
RAISE DEBUG 'hskip: %', hskip;
hstart := xoff + hskip*hstep;
RAISE DEBUG 'hstart: %', hstart;
-- Tweak vertical start on hstep grid from origin
vstart := yoff + ceil((@postgisschema@.ST_Ymin(ext)-yoff)/vstep)*vstep;
vstart := yoff + ceil((ST_Ymin(ext)-yoff)/vstep)*vstep;
RAISE DEBUG 'vstart: %', vstart;
hend := @postgisschema@.ST_XMax(ext);
vend := @postgisschema@.ST_YMax(ext);
hend := ST_XMax(ext);
vend := ST_YMax(ext);
IF vstart - (vstep/2.0) < @postgisschema@.ST_YMin(ext) THEN
IF vstart - (vstep/2.0) < ST_YMin(ext) THEN
vstartary := ARRAY[ vstart + (vstep/2.0), vstart ];
ELSE
vstartary := ARRAY[ vstart - (vstep/2.0), vstart ];
END IF;
If maxcells IS NOT NULL AND maxcells > 0 THEN
IF CEIL((CEIL((vend-vstart)/(vstep/2.0)) * CEIL((hend-hstart)/(hstep*2.0/3.0)))/3.0)::integer > maxcells THEN
RAISE EXCEPTION 'The requested grid is too big to be rendered';
END IF;
END IF;
vstartidx := abs(hskip)%2;
RAISE DEBUG 'vstartary: % : %', vstartary[1], vstartary[2];
RAISE DEBUG 'vstartidx: %', vstartidx;
c := @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(hstart, vstartary[vstartidx+1]), srid);
h := @postgisschema@.ST_SnapToGrid(@extschema@.CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
c := ST_SetSRID(ST_MakePoint(hstart, vstartary[vstartidx+1]), srid);
h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
vstartidx := (vstartidx + 1) % 2;
WHILE @postgisschema@.ST_X(c) < hend LOOP -- over X
WHILE ST_X(c) < hend LOOP -- over X
--RAISE DEBUG 'X loop starts, center point: %', ST_AsText(c);
WHILE @postgisschema@.ST_Y(c) < vend LOOP -- over Y
WHILE ST_Y(c) < vend LOOP -- over Y
--RAISE DEBUG 'Center: %', ST_AsText(c);
--h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
RETURN NEXT h;
h := @postgisschema@.ST_SnapToGrid(ST_Translate(h, 0, vstep), xoff, yoff, xgrd, ygrd);
c := @postgisschema@.ST_Translate(c, 0, vstep); -- TODO: drop ?
h := ST_SnapToGrid(ST_Translate(h, 0, vstep), xoff, yoff, xgrd, ygrd);
c := ST_Translate(c, 0, vstep); -- TODO: drop ?
END LOOP;
-- TODO: translate h direcly ...
c := @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(ST_X(c)+hstep, vstartary[vstartidx+1]), srid);
h := @postgisschema@.ST_SnapToGrid(@extschema@.CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
c := ST_SetSRID(ST_MakePoint(ST_X(c)+hstep, vstartary[vstartidx+1]), srid);
h := ST_SnapToGrid(CDB_MakeHexagon(c, side), xoff, yoff, xgrd, ygrd);
vstartidx := (vstartidx + 1) % 2;
END LOOP;
RETURN;
END
$$ LANGUAGE 'plpgsql' IMMUTABLE PARALLEL SAFE;
$$ LANGUAGE 'plpgsql' IMMUTABLE;

View File

@ -10,337 +10,212 @@
--
-- @param invert Optional wheter to return the top of each bin (default)
-- or the bottom. BOOLEAN, default=FALSE.
--
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_JenksBins(in_array NUMERIC[], breaks INT, iterations INT DEFAULT 0, invert BOOLEAN DEFAULT FALSE)
RETURNS NUMERIC[] as
$$
DECLARE
in_matrix NUMERIC[][];
in_unique_count BIGINT;
shuffles INT;
CREATE OR REPLACE FUNCTION CDB_JenksBins ( in_array NUMERIC[], breaks INT, iterations INT DEFAULT 5, invert BOOLEAN DEFAULT FALSE) RETURNS NUMERIC[] as $$
DECLARE
element_count INT4;
arr_mean NUMERIC;
sdam NUMERIC;
i INT;
bot INT;
top INT;
tops INT[];
classes INT[][];
j INT := 1;
i INT := 1; j INT := 1;
curr_result NUMERIC[];
best_result NUMERIC[];
seedtarget TEXT;
quant NUMERIC[];
shuffles INT;
BEGIN
-- We clean the input array (remove NULLs) and create 2 arrays
-- [1] contains the unique values in in_array
-- [2] contains the number of appearances of those unique values
SELECT ARRAY[array_agg(value), array_agg(count)] FROM
(
SELECT value, count(1)::numeric as count
FROM unnest(in_array) AS value
WHERE value is NOT NULL
GROUP BY value
ORDER BY value
) __clean_array_q INTO in_matrix;
-- get the total size of our row
element_count := array_length(in_array, 1); --array_upper(in_array, 1) - array_lower(in_array, 1);
-- ensure the ordering of in_array
SELECT array_agg(e) INTO in_array FROM (SELECT unnest(in_array) e ORDER BY e) x;
-- stop if no rows
IF element_count IS NULL THEN
RETURN NULL;
END IF;
-- stop if our breaks are more than our input array size
IF element_count < breaks THEN
RETURN in_array;
END IF;
-- Get the number of unique values
in_unique_count := array_length(in_matrix[1:1], 2);
shuffles := LEAST(GREATEST(floor(2500000.0/(element_count::float*iterations::float)), 1), 750)::int;
-- get our mean value
SELECT avg(v) INTO arr_mean FROM ( SELECT unnest(in_array) as v ) x;
IF in_unique_count IS NULL THEN
RETURN NULL;
-- assume best is actually Quantile
SELECT CDB_QuantileBins(in_array, breaks) INTO quant;
-- if data is very very large, just return quant and be done
IF element_count > 5000000 THEN
RETURN quant;
END IF;
IF in_unique_count <= breaks THEN
-- There isn't enough distinct values for the requested breaks
RETURN ARRAY(Select unnest(in_matrix[1:1])) _a;
END IF;
-- If not declated explicitly we iterate based on the length of the array
IF iterations < 1 THEN
-- This is based on a 'looks fine' heuristic
iterations := log(in_unique_count)::integer + 1;
END IF;
-- We set the number of shuffles per iteration as the number of unique values but
-- this is just another 'looks fine' heuristic
shuffles := in_unique_count;
-- Get the mean value of the whole vector (already ignores NULLs)
SELECT avg(v) INTO arr_mean FROM ( SELECT unnest(in_array) as v ) x;
-- Calculate the sum of squared deviations from the array mean (SDAM).
SELECT sum(((arr_mean - v)^2) * w) INTO sdam FROM (
SELECT unnest(in_matrix[1:1]) as v, unnest(in_matrix[2:2]) as w
) x;
-- To start, we create ranges with approximately the same amount of different values
top := 0;
i := 1;
LOOP
bot := top + 1;
top := ROUND(i * in_unique_count::numeric / breaks::NUMERIC);
IF i = 1 THEN
classes = ARRAY[ARRAY[bot,top]];
ELSE
classes = ARRAY_CAT(classes, ARRAY[bot,top]);
-- change quant into bottom, top markers
LOOP
IF i = 1 THEN
bot = 1;
ELSE
-- use last top to find this bot
bot = top+1;
END IF;
IF i = breaks THEN
top = element_count;
ELSE
SELECT count(*) INTO top FROM ( SELECT unnest(in_array) as v) x WHERE v <= quant[i];
END IF;
IF i = 1 THEN
classes = ARRAY[ARRAY[bot,top]];
ELSE
classes = ARRAY_CAT(classes,ARRAY[bot,top]);
END IF;
i := i + 1;
IF i > breaks THEN EXIT; END IF;
i = i+1;
END LOOP;
best_result = @extschema@.CDB_JenksBinsIteration(in_matrix, breaks, classes, invert, sdam, shuffles);
best_result = CDB_JenksBinsIteration( in_array, breaks, classes, invert, element_count, arr_mean, shuffles);
--set the seed so we can ensure the same results
SELECT setseed(0.4567) INTO seedtarget;
--loop through random starting positions
LOOP
IF j > iterations-1 THEN EXIT; END IF;
IF j > iterations-1 THEN EXIT; END IF;
i = 1;
tops = ARRAY[in_unique_count];
tops = ARRAY[element_count];
LOOP
IF i = breaks THEN EXIT; END IF;
SELECT array_agg(distinct e) INTO tops FROM (
SELECT unnest(array_cat(tops, ARRAY[trunc(random() * in_unique_count::float8)::int + 1])) as e ORDER BY e
) x;
i = array_length(tops, 1);
END LOOP;
top := 0;
IF i = breaks THEN EXIT; END IF;
SELECT array_agg(distinct e) INTO tops FROM (SELECT unnest(array_cat(tops, ARRAY[floor(random()*element_count::float)::int])) as e ORDER BY e) x WHERE e != 1;
i = array_length(tops, 1);
END LOOP;
i = 1;
LOOP
bot := top + 1;
top = tops[i];
LOOP
IF i > breaks THEN EXIT; END IF;
IF i = 1 THEN
classes = ARRAY[ARRAY[bot,top]];
bot = 1;
ELSE
classes = ARRAY_CAT(classes, ARRAY[bot,top]);
bot = top+1;
END IF;
i := i+1;
IF i > breaks THEN EXIT; END IF;
END LOOP;
curr_result = @extschema@.CDB_JenksBinsIteration(in_matrix, breaks, classes, invert, sdam, shuffles);
top = tops[i];
IF i = 1 THEN
classes = ARRAY[ARRAY[bot,top]];
ELSE
classes = ARRAY_CAT(classes,ARRAY[bot,top]);
END IF;
i := i+1;
END LOOP;
curr_result = CDB_JenksBinsIteration( in_array, breaks, classes, invert, element_count, arr_mean, shuffles);
IF curr_result[1] > best_result[1] THEN
best_result = curr_result;
j = j-1; -- if we found a better result, add one more search
END IF;
j = j+1;
END LOOP;
RETURN (best_result)[2:array_upper(best_result, 1)];
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL RESTRICTED;
$$ language plpgsql IMMUTABLE;
--
-- Perform a single iteration of the Jenks classification
--
-- Returns an array with:
-- - First element: gvf
-- - Second to 2+n: Category limits
DROP FUNCTION IF EXISTS @extschema@.CDB_JenksBinsIteration ( in_matrix NUMERIC[], breaks INT, classes INT[], invert BOOLEAN, element_count INT4, arr_mean NUMERIC, max_search INT); -- Old signature
CREATE OR REPLACE FUNCTION @extschema@.CDB_JenksBinsIteration ( in_matrix NUMERIC[], breaks INT, classes INT[], invert BOOLEAN, sdam NUMERIC, max_search INT DEFAULT 50) RETURNS NUMERIC[] as $$
DECLARE
i INT;
iterations INT = 0;
CREATE OR REPLACE FUNCTION CDB_JenksBinsIteration ( in_array NUMERIC[], breaks INT, classes INT[][], invert BOOLEAN, element_count INT4, arr_mean NUMERIC, max_search INT DEFAULT 50) RETURNS NUMERIC[] as $$
DECLARE
tmp_val numeric;
new_classes int[][];
tmp_class int[];
i INT := 1;
j INT := 1;
side INT := 2;
sdam numeric;
gvf numeric := 0.0;
new_gvf numeric;
arr_gvf numeric[];
class_avg numeric;
class_max_i INT;
class_min_i INT;
class_max numeric;
class_min numeric;
reply numeric[];
BEGIN
gvf numeric := 0.0;
new_gvf numeric;
arr_gvf numeric[];
arr_avg numeric[];
class_avg numeric;
class_dev numeric;
class_max_i INT = 0;
class_min_i INT = 0;
dev_max numeric;
dev_min numeric;
best_classes INT[] = classes;
best_gvf numeric[];
best_avg numeric[];
move_elements INT = 1;
reply numeric[];
BEGIN
-- We fill the arrays with the initial values
i = 0;
LOOP
IF i = breaks THEN EXIT; END IF;
i = i + 1;
-- Get class mean
SELECT (sum(v * w) / sum(w)) INTO class_avg FROM (
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
) x;
-- Get class deviation
SELECT sum((class_avg - v)^2 * w) INTO class_dev FROM (
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
) x;
IF i = 1 THEN
arr_avg = ARRAY[class_avg];
arr_gvf = ARRAY[class_dev];
ELSE
arr_avg = array_append(arr_avg, class_avg);
arr_gvf = array_append(arr_gvf, class_dev);
END IF;
END LOOP;
-- We copy the values to avoid recalculation when a failure happens
best_avg = arr_avg;
best_gvf = arr_gvf;
iterations = 0;
LOOP
IF iterations = max_search THEN EXIT; END IF;
iterations = iterations + 1;
-- Calculate the sum of squared deviations from the array mean (SDAM).
SELECT sum((arr_mean - e)^2) INTO sdam FROM ( SELECT unnest(in_array) as e ) x;
--Identify the breaks for the lowest GVF
LOOP
i = 1;
LOOP
-- get our mean
SELECT avg(e) INTO class_avg FROM ( SELECT unnest(in_array[classes[i][1]:classes[i][2]]) as e) x;
-- find the deviation
SELECT sum((class_avg-e)^2) INTO tmp_val FROM ( SELECT unnest(in_array[classes[i][1]:classes[i][2]]) as e ) x;
IF i = 1 THEN
arr_gvf = ARRAY[tmp_val];
-- init our min/max map for later
class_max = arr_gvf[i];
class_min = arr_gvf[i];
class_min_i = 1;
class_max_i = 1;
ELSE
arr_gvf = array_append(arr_gvf, tmp_val);
END IF;
i := i+1;
IF i > breaks THEN EXIT; END IF;
END LOOP;
-- calculate our new GVF
SELECT sdam - sum(e) INTO new_gvf FROM ( SELECT unnest(arr_gvf) as e ) x;
-- Check if any improvement was made
IF new_gvf <= gvf THEN
-- If we were moving too many elements, go back and move less
IF move_elements <= 2 OR class_max_i = class_min_i THEN
EXIT;
END IF;
move_elements = GREATEST(move_elements / 8, 1);
-- Rollback from saved statuses
classes = best_classes;
new_gvf = gvf;
i = class_min_i;
LOOP
arr_avg[i] = best_avg[i];
arr_gvf[i] = best_gvf[i];
IF i = class_max_i THEN EXIT; END IF;
i = i + 1;
END LOOP;
END IF;
-- We search for the classes with the min and max deviation
i = 1;
class_min_i = 1;
class_max_i = 1;
dev_max = arr_gvf[1];
dev_min = arr_gvf[1];
LOOP
IF i = breaks THEN EXIT; END IF;
i = i + 1;
IF arr_gvf[i] < dev_min THEN
dev_min = arr_gvf[i];
class_min_i = i;
ELSE
IF arr_gvf[i] > dev_max THEN
dev_max = arr_gvf[i];
class_max_i = i;
END IF;
END IF;
END LOOP;
-- Save best values for comparison and output
gvf = new_gvf;
best_classes = classes;
-- Limit the moved elements as to not remove everything from class_max_i
move_elements = LEAST(move_elements, classes[class_max_i][2] - classes[class_max_i][1]);
-- Move `move_elements` from class_max_i to class_min_i
IF class_min_i < class_max_i THEN
i := class_min_i;
LOOP
IF i = class_max_i THEN EXIT; END IF;
classes[i][2] = classes[i][2] + move_elements;
i := i + 1;
END LOOP;
i := class_max_i;
LOOP
IF i = class_min_i THEN EXIT; END IF;
classes[i][1] = classes[i][1] + move_elements;
i := i - 1;
END LOOP;
SELECT sdam-sum(e) INTO new_gvf FROM ( SELECT unnest(arr_gvf) as e ) x;
-- if no improvement was made, exit
IF new_gvf < gvf THEN EXIT; END IF;
gvf = new_gvf;
IF j > max_search THEN EXIT; END IF;
j = j+1;
i = 1;
LOOP
--establish directionality (uppward through classes or downward)
IF arr_gvf[i] < class_min THEN
class_min = arr_gvf[i];
class_min_i = i;
END IF;
IF arr_gvf[i] > class_max THEN
class_max = arr_gvf[i];
class_max_i = i;
END IF;
i := i+1;
IF i > breaks THEN EXIT; END IF;
END LOOP;
IF class_max_i > class_min_i THEN
class_min_i = class_max_i - 1;
ELSE
i := class_min_i;
LOOP
IF i = class_max_i THEN EXIT; END IF;
classes[i][1] = classes[i][1] - move_elements;
i := i - 1;
END LOOP;
i := class_max_i;
LOOP
IF i = class_min_i THEN EXIT; END IF;
classes[i][2] = classes[i][2] - move_elements;
i := i + 1;
END LOOP;
class_min_i = class_max_i + 1;
END IF;
-- Recalculate avg and deviation ONLY for the affected classes
i = LEAST(class_min_i, class_max_i);
class_max_i = GREATEST(class_min_i, class_max_i);
class_min_i = i;
LOOP
SELECT (sum(v * w) / sum(w)) INTO class_avg FROM (
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
) x;
SELECT sum((class_avg - v)^2 * w) INTO class_dev FROM (
SELECT unnest(in_matrix[1:1][classes[i][1]:classes[i][2]]) as v,
unnest(in_matrix[2:2][classes[i][1]:classes[i][2]]) as w
) x;
-- Save status (in case it's needed for rollback) and store the new one
best_avg[i] = arr_avg[i];
arr_avg[i] = class_avg;
best_gvf[i] = arr_gvf[i];
arr_gvf[i] = class_dev;
IF i = class_max_i THEN EXIT; END IF;
i = i + 1;
END LOOP;
move_elements = move_elements * 2;
END LOOP;
--Move from higher class to a lower gid order
IF class_max_i > class_min_i THEN
classes[class_max_i][1] = classes[class_max_i][1] + 1;
classes[class_min_i][2] = classes[class_min_i][2] + 1;
ELSE -- Move from lower class UP into a higher class by gid
classes[class_max_i][2] = classes[class_max_i][2] - 1;
classes[class_min_i][1] = classes[class_min_i][1] - 1;
END IF;
END LOOP;
i = 1;
LOOP
LOOP
IF invert = TRUE THEN
side = 1; --default returns bottom side of breaks, invert returns top side
END IF;
reply = array_append(reply, unnest(in_matrix[1:1][best_classes[i][side]:best_classes[i][side]]));
i = i+1;
IF i > breaks THEN EXIT; END IF;
END LOOP;
reply = array_append(reply, in_array[classes[i][side]]);
i = i+1;
IF i > breaks THEN EXIT; END IF;
END LOOP;
RETURN array_prepend(gvf, reply);
reply = array_prepend(gvf, reply);
RETURN reply;
END;
$$ language plpgsql IMMUTABLE;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;

View File

@ -7,11 +7,13 @@
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_LatLng (lat NUMERIC, lng NUMERIC) RETURNS @postgisschema@.geometry as $$
SELECT @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(lng,lat), 4326);
$$ language SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION CDB_LatLng (lat NUMERIC, lng NUMERIC) RETURNS geometry as $$
-- this function is silly
SELECT ST_SetSRID(ST_MakePoint(lng,lat),4326);
$$ language SQL IMMUTABLE;
CREATE OR REPLACE FUNCTION @extschema@.CDB_LatLng (lat FLOAT8, lng FLOAT8) RETURNS @postgisschema@.geometry as $$
SELECT @postgisschema@.ST_SetSRID(@postgisschema@.ST_MakePoint(lng,lat), 4326);
$$ language SQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION CDB_LatLng (lat FLOAT8, lng FLOAT8) RETURNS geometry as $$
-- this function is silly
SELECT ST_SetSRID(ST_MakePoint(lng,lat),4326);
$$ language SQL IMMUTABLE;

View File

@ -4,7 +4,7 @@
-- Mode
-- https://wiki.postgresql.org/wiki/Aggregate_Mode
CREATE OR REPLACE FUNCTION @extschema@._CDB_Math_final_mode(anyarray)
CREATE OR REPLACE FUNCTION cartodb._CDB_Math_final_mode(anyarray)
RETURNS anyelement AS
$BODY$
SELECT a
@ -13,15 +13,14 @@ $BODY$
ORDER BY COUNT(1) DESC, 1
LIMIT 1;
$BODY$
LANGUAGE 'sql' IMMUTABLE PARALLEL SAFE;
LANGUAGE 'sql' IMMUTABLE;
DROP AGGREGATE IF EXISTS @extschema@.CDB_Math_Mode(anyelement);
DROP AGGREGATE IF EXISTS cartodb.CDB_Math_Mode(anyelement);
CREATE AGGREGATE @extschema@.CDB_Math_Mode(anyelement) (
CREATE AGGREGATE cartodb.CDB_Math_Mode(anyelement) (
SFUNC=array_append,
STYPE=anyarray,
FINALFUNC=@extschema@._CDB_Math_final_mode,
PARALLEL = SAFE,
FINALFUNC=_CDB_Math_final_mode,
INITCOND='{}'
);

View File

@ -1,65 +0,0 @@
-- Function that reassign the owner of a table to their ownership_role
CREATE OR REPLACE FUNCTION @extschema@.CDB_OAuthReassignTableOwnerOnCreation()
RETURNS event_trigger
AS $$
DECLARE
obj record;
owner_role text;
creator_role text;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
RAISE DEBUG '% ddl object: % % % %',
tg_tag,
obj.command_tag,
obj.object_type,
obj.schema_name,
obj.object_identity;
IF obj.object_type = 'function' THEN
SELECT rolname FROM pg_proc JOIN pg_roles ON proowner = pg_roles.oid WHERE pg_proc.oid = obj.objid INTO creator_role;
ELSE
SELECT rolname FROM pg_class JOIN pg_roles ON relowner = pg_roles.oid WHERE pg_class.oid = obj.objid INTO creator_role;
END IF;
SELECT value->>'ownership_role_name' from @extschema@.CDB_Conf_GetConf('api_keys_' || quote_ident(creator_role)) value INTO owner_role;
IF owner_role IS NULL OR owner_role = '' THEN
RAISE DEBUG 'owner_role not found';
CONTINUE;
ELSE
EXECUTE 'ALTER ' || obj.object_type || ' ' || obj.object_identity || ' OWNER TO ' || quote_ident(owner_role);
IF obj.object_type = 'function' THEN
EXECUTE 'GRANT ALL ON FUNCTION ' || obj.object_identity || ' TO ' || QUOTE_IDENT(creator_role);
ELSE
EXECUTE 'GRANT ALL ON ' || obj.object_identity || ' TO ' || QUOTE_IDENT(creator_role);
END IF;
RAISE DEBUG 'Changing ownership from % to %', creator_role, owner_role;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
-- Creates the trigger on DDL events in order to reassign the owner
CREATE OR REPLACE FUNCTION @extschema@.CDB_EnableOAuthReassignTablesTrigger()
RETURNS void
AS $$
BEGIN
DROP EVENT TRIGGER IF EXISTS oauth_reassign_tables_trigger;
CREATE EVENT TRIGGER oauth_reassign_tables_trigger
ON ddl_command_end
WHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'CREATE VIEW', 'CREATE FOREIGN TABLE', 'CREATE MATERIALIZED VIEW', 'CREATE SEQUENCE', 'CREATE FUNCTION')
EXECUTE PROCEDURE @extschema@.CDB_OAuthReassignTableOwnerOnCreation();
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- Deletes the trigger on DDL events in order to reassign the owner
CREATE OR REPLACE FUNCTION @extschema@.CDB_DisableOAuthReassignTablesTrigger()
RETURNS void
AS $$
BEGIN
DROP EVENT TRIGGER IF EXISTS oauth_reassign_tables_trigger;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;

View File

@ -1,23 +1,16 @@
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Member_Group_Role_Member_Name()
FUNCTION cartodb.CDB_Organization_Member_Group_Role_Member_Name()
RETURNS TEXT
AS $$
SELECT 'cdb_org_member'::text || '_' || md5(current_database());
$$
LANGUAGE SQL STABLE PARALLEL SAFE;
LANGUAGE SQL IMMUTABLE;
----- ########################## WARNING ##########################
----- The code below creates a new role for the organization but
----- only when the extension is INSTALLED in a database, i.e. it
----- won't work if you clone a database that has it installed.
----- If you do, you need to update the extension to next and back
----- ########################## WARNING ##########################
DO LANGUAGE 'plpgsql' $$
DECLARE
cdb_org_member_role_name TEXT;
BEGIN
cdb_org_member_role_name := @extschema@.CDB_Organization_Member_Group_Role_Member_Name();
cdb_org_member_role_name := cartodb.CDB_Organization_Member_Group_Role_Member_Name();
IF NOT EXISTS ( SELECT * FROM pg_roles WHERE rolname= cdb_org_member_role_name )
THEN
EXECUTE 'CREATE ROLE "' || cdb_org_member_role_name || '" NOLOGIN;';
@ -26,37 +19,31 @@ END
$$;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Create_Member(role_name text)
FUNCTION cartodb.CDB_Organization_Create_Member(role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'GRANT "' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || '" TO "' || role_name || '"';
EXECUTE 'GRANT "' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || '" TO "' || role_name || '"';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
-------------------------------------------------------------------------------
-- Administrator
-------------------------------------------------------------------------------
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Organization_Admin_Role_Name()
FUNCTION cartodb._CDB_Organization_Admin_Role_Name()
RETURNS TEXT
AS $$
SELECT current_database() || '_a'::text;
$$
LANGUAGE SQL STABLE PARALLEL SAFE;
LANGUAGE SQL IMMUTABLE;
----- ########################## WARNING ##########################
----- The code below creates a new role for the organization but
----- only when the extension is INSTALLED in a database, i.e. it
----- won't work if you clone a database that has it installed.
----- If you do, you need to update the extension to next and back
----- ########################## WARNING ##########################
-- Administrator role creation on extension install
DO LANGUAGE 'plpgsql' $$
DECLARE
cdb_org_admin_role_name TEXT;
BEGIN
cdb_org_admin_role_name := @extschema@._CDB_Organization_Admin_Role_Name();
cdb_org_admin_role_name := cartodb._CDB_Organization_Admin_Role_Name();
IF NOT EXISTS ( SELECT * FROM pg_roles WHERE rolname= cdb_org_admin_role_name )
THEN
EXECUTE format('CREATE ROLE %I CREATEROLE NOLOGIN;', cdb_org_admin_role_name);
@ -65,105 +52,80 @@ END
$$;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_AddAdmin(username text)
FUNCTION cartodb.CDB_Organization_AddAdmin(username text)
RETURNS void
AS $$
DECLARE
cdb_user_role TEXT;
cdb_admin_role TEXT;
BEGIN
cdb_admin_role := @extschema@._CDB_Organization_Admin_Role_Name();
cdb_user_role := @extschema@._CDB_User_RoleFromUsername(username);
cdb_admin_role := cartodb._CDB_Organization_Admin_Role_Name();
cdb_user_role := cartodb._CDB_User_RoleFromUsername(username);
EXECUTE format('GRANT %I TO %I WITH ADMIN OPTION', cdb_admin_role, cdb_user_role);
-- CREATEROLE is not inherited, and is needed for user creation
EXECUTE format('ALTER ROLE %I CREATEROLE', cdb_user_role);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_RemoveAdmin(username text)
FUNCTION cartodb.CDB_Organization_RemoveAdmin(username text)
RETURNS void
AS $$
DECLARE
cdb_user_role TEXT;
cdb_admin_role TEXT;
BEGIN
cdb_admin_role := @extschema@._CDB_Organization_Admin_Role_Name();
cdb_user_role := @extschema@._CDB_User_RoleFromUsername(username);
cdb_admin_role := cartodb._CDB_Organization_Admin_Role_Name();
cdb_user_role := cartodb._CDB_User_RoleFromUsername(username);
EXECUTE format('ALTER ROLE %I NOCREATEROLE', cdb_user_role);
EXECUTE format('REVOKE %I FROM %I', cdb_admin_role, cdb_user_role);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL;
-------------------------------------------------------------------------------
-- Sharing tables
-------------------------------------------------------------------------------
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Add_Table_Read_Permission(from_schema text, table_name text, to_role_name text)
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'GRANT USAGE ON SCHEMA "' || from_schema || '" TO "' || to_role_name || '"';
EXECUTE 'GRANT SELECT ON "' || from_schema || '"."' || table_name || '" TO "' || to_role_name || '"';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Add_Table_Organization_Read_Permission(from_schema text, table_name text)
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT @extschema@.CDB_Organization_Add_Table_Read_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
EXECUTE 'SELECT cartodb.CDB_Organization_Add_Table_Read_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@._CDB_Organization_Get_Table_Sequences(from_schema text, table_name text)
RETURNS SETOF TEXT
AS $$
BEGIN
RETURN QUERY EXECUTE 'SELECT
quote_ident(n.nspname) || ''.'' || quote_ident(c.relname)
FROM
pg_depend d
JOIN pg_class c ON d.objid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE
d.refobjsubid > 0 AND
d.classid = ''pg_class''::regclass AND
c.relkind = ''S''::"char" AND
d.refobjid = (''' || quote_ident(from_schema) || '.' || quote_ident(table_name) ||''')::regclass';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Add_Table_Read_Write_Permission(from_schema text, table_name text, to_role_name text)
FUNCTION cartodb.CDB_Organization_Add_Table_Read_Write_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
DECLARE
sequence_name TEXT;
BEGIN
EXECUTE 'GRANT USAGE ON SCHEMA "' || from_schema || '" TO "' || to_role_name || '"';
EXECUTE 'GRANT SELECT, INSERT, UPDATE, DELETE ON "' || from_schema || '"."' || table_name || '" TO "' || to_role_name || '"';
FOR sequence_name IN SELECT * FROM @extschema@._CDB_Organization_Get_Table_Sequences(from_schema, table_name) LOOP
EXECUTE 'GRANT USAGE, SELECT ON SEQUENCE ' || sequence_name || ' TO "' || to_role_name || '"';
END LOOP;
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Add_Table_Organization_Read_Write_Permission(from_schema text, table_name text)
FUNCTION cartodb.CDB_Organization_Add_Table_Organization_Read_Write_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT @extschema@.CDB_Organization_Add_Table_Read_Write_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
EXECUTE 'SELECT cartodb.CDB_Organization_Add_Table_Read_Write_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Remove_Access_Permission(from_schema text, table_name text, to_role_name text)
FUNCTION cartodb.CDB_Organization_Remove_Access_Permission(from_schema text, table_name text, to_role_name text)
RETURNS void
AS $$
BEGIN
@ -172,20 +134,13 @@ BEGIN
-- We need to revoke usage on schema only if we are revoking privileges from the last table where to_role_name has
-- any permission granted within the schema from_schema
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
$$ LANGUAGE PLPGSQL VOLATILE;
CREATE OR REPLACE
FUNCTION @extschema@.CDB_Organization_Remove_Organization_Access_Permission(from_schema text, table_name text)
FUNCTION cartodb.CDB_Organization_Remove_Organization_Access_Permission(from_schema text, table_name text)
RETURNS void
AS $$
BEGIN
EXECUTE 'SELECT @extschema@.CDB_Organization_Remove_Access_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || @extschema@.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
EXECUTE 'SELECT cartodb.CDB_Organization_Remove_Access_Permission(''' || from_schema || ''', ''' || table_name || ''', ''' || cartodb.CDB_Organization_Member_Group_Role_Member_Name() || ''');';
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
--------------------------------------------------------------------------------
-- Deprecated
--------------------------------------------------------------------------------
DROP FUNCTION IF EXISTS @extschema@.CDB_Organization_Grant_Role(name);
DROP FUNCTION IF EXISTS @extschema@.CDB_Organization_Revoke_Role(name);
$$ LANGUAGE PLPGSQL VOLATILE;

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +0,0 @@
-- Auxiliary overviews FUNCTIONS
-- Maximum zoom level for which overviews may be created
CREATE OR REPLACE FUNCTION @extschema@._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 PARALLEL SAFE;
-- Maximum zoom level usable with integer coordinates
CREATE OR REPLACE FUNCTION @extschema@._CDB_MaxZoomLevel()
RETURNS INTEGER
AS $$
BEGIN
RETURN 31;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- 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 @extschema@._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', '@extschema@')
ELSE n.nspname = schema_name
END;
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
-- Pattern that can be used to detect overview tables and Extract
-- the intended zoom level from the table name.
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableDiscriminator()
RETURNS TEXT
AS $$
BEGIN
RETURN '\A_vovw_(\d+)_';
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- substring(tablename from _CDB_OverviewTableDiscriminator())
-- Pattern matched by the overview tables of a given base table name.
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTablePattern(base_table TEXT)
RETURNS TEXT
AS $$
BEGIN
RETURN @extschema@._CDB_OverviewTableDiscriminator() || base_table;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- tablename SIMILAR TO _CDB_OverviewTablePattern(base_table)
-- Name of an overview table, given the base table name and the Z level
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableName(base_table TEXT, z INTEGER)
RETURNS TEXT
AS $$
BEGIN
RETURN '_vovw_' || z::text || '_' || base_table;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- Condition to check if a tabla is an overview table of some base table
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_IsOverviewTableOf(base_table TEXT, otable TEXT)
RETURNS BOOLEAN
AS $$
BEGIN
RETURN otable SIMILAR TO @extschema@._CDB_OverviewTablePattern(base_table);
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- Extract the Z level from an overview table name
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewTableZ(otable TEXT)
RETURNS INTEGER
AS $$
BEGIN
RETURN substring(otable from @extschema@._CDB_OverviewTableDiscriminator())::integer;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- Name of the base table corresponding to an overview table
-- Scope: private.
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewBaseTableName(overview_table TEXT)
RETURNS TEXT
AS $$
BEGIN
IF @extschema@._CDB_OverviewTableZ(overview_table) IS NULL THEN
RETURN overview_table;
ELSE
RETURN regexp_replace(overview_table, @extschema@._CDB_OverviewTableDiscriminator(), '');
END IF;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION @extschema@._CDB_OverviewBaseTable(overview_table REGCLASS)
RETURNS REGCLASS
AS $$
DECLARE
table_name TEXT;
schema_name TEXT;
base_name TEXT;
base_table REGCLASS;
BEGIN
SELECT * FROM @extschema@._cdb_split_table_name(overview_table) INTO schema_name, table_name;
base_name := @extschema@._CDB_OverviewBaseTableName(table_name);
IF base_name != table_name THEN
base_table := Format('%I.%I', schema_name, base_name)::regclass;
ELSE
base_table := overview_table;
END IF;
RETURN base_table;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE PARALLEL SAFE;
-- 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 @extschema@._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 PARALLEL SAFE;
-- 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 @extschema@._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 PARALLEL SAFE;

View File

@ -5,14 +5,39 @@
-- bins based on the Quantile method.
--
-- @param breaks The number of bins you want to find.
--
--
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_QuantileBins(in_array numeric[], breaks int)
RETURNS numeric[]
AS $$
SELECT
percentile_disc(Array(SELECT generate_series(1, breaks) / breaks::numeric))
WITHIN GROUP (ORDER BY x ASC) AS p
FROM
unnest(in_array) AS x;
$$ language SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE OR REPLACE FUNCTION CDB_QuantileBins ( in_array NUMERIC[], breaks INT) RETURNS NUMERIC[] as $$
DECLARE
element_count INT4;
break_size numeric;
tmp_val numeric;
i INT := 1;
reply numeric[];
BEGIN
-- sort our values
SELECT array_agg(e) INTO in_array FROM (SELECT unnest(in_array) e ORDER BY e ASC) x;
-- get the total size of our data
element_count := array_length(in_array, 1);
break_size := element_count::numeric / breaks;
-- slice our bread
LOOP
IF i < breaks THEN
IF break_size * i % 1 > 0 THEN
SELECT e INTO tmp_val FROM ( SELECT unnest(in_array) e LIMIT 1 OFFSET ceil(break_size * i) - 1) x;
ELSE
SELECT avg(e) INTO tmp_val FROM ( SELECT unnest(in_array) e LIMIT 2 OFFSET ceil(break_size * i) - 1 ) x;
END IF;
ELSIF i = breaks THEN
-- select the last value
SELECT max(e) INTO tmp_val FROM ( SELECT unnest(in_array) e ) x;
ELSE
EXIT;
END IF;
reply = array_append(reply, tmp_val);
i := i+1;
END LOOP;
RETURN reply;
END;
$$ language plpgsql IMMUTABLE;

View File

@ -3,7 +3,7 @@
-- Regexp curtesy of Hubert Lubaczewski (depesz)
-- Implemented in plpython for performance reasons
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryStatements(query text)
CREATE OR REPLACE FUNCTION CDB_QueryStatements(query text)
RETURNS SETOF TEXT AS $$
import re
pat = re.compile( r'''((?:[^'"$;]+|"[^"]*"|'[^']*'|(\$[^$]*\$).*?\2)+)''', re.DOTALL )
@ -11,4 +11,4 @@ RETURNS SETOF TEXT AS $$
cleaned = match[0].strip()
if ( cleaned ):
yield cleaned
$$ language '@@plpythonu@@' IMMUTABLE STRICT PARALLEL SAFE;
$$ language 'plpythonu' IMMUTABLE STRICT;

View File

@ -2,7 +2,7 @@
--
-- Requires PostgreSQL 9.x+
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryTablesText(query text)
CREATE OR REPLACE FUNCTION CDB_QueryTablesText(query text)
RETURNS text[]
AS $$
DECLARE
@ -11,16 +11,19 @@ DECLARE
rec RECORD;
rec2 RECORD;
BEGIN
tables := '{}';
FOR rec IN SELECT @extschema@.CDB_QueryStatements(query) q LOOP
FOR rec IN SELECT CDB_QueryStatements(query) q LOOP
IF NOT ( rec.q ilike 'select%' or rec.q ilike 'with%' ) THEN
--RAISE WARNING 'Skipping %', rec.q;
CONTINUE;
END IF;
BEGIN
EXECUTE 'EXPLAIN (FORMAT XML, VERBOSE) ' || rec.q INTO STRICT exp;
EXCEPTION WHEN syntax_error THEN
-- We can get a syntax error if the user tries to EXPLAIN a DDL
CONTINUE;
WHEN others THEN
EXCEPTION WHEN others THEN
-- TODO: if error is 'relation "xxxxxx" does not exist', take xxxxxx as
-- the affected table ?
RAISE WARNING 'CDB_QueryTables cannot explain query: % (%: %)', rec.q, SQLSTATE, SQLERRM;
@ -61,15 +64,15 @@ BEGIN
return tables;
END
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
$$ LANGUAGE 'plpgsql' VOLATILE STRICT;
-- Keep CDB_QueryTables with same signature for backwards compatibility.
-- It should probably be removed in the future.
CREATE OR REPLACE FUNCTION @extschema@.CDB_QueryTables(query text)
CREATE OR REPLACE FUNCTION CDB_QueryTables(query text)
RETURNS name[]
AS $$
BEGIN
RETURN @extschema@.CDB_QueryTablesText(query)::name[];
RETURN CDB_QueryTablesText(query)::name[];
END
$$ LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
$$ LANGUAGE 'plpgsql' VOLATILE STRICT;

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION @extschema@._CDB_total_relation_size(_schema_name TEXT, _table_name TEXT)
CREATE OR REPLACE FUNCTION cartodb._CDB_total_relation_size(_schema_name TEXT, _table_name TEXT)
RETURNS bigint AS
$$
DECLARE relation_size bigint := 0;
@ -7,63 +7,47 @@ BEGIN
SELECT pg_total_relation_size(format('"%s"."%s"', _schema_name, _table_name)) INTO relation_size;
EXCEPTION
WHEN undefined_table OR OTHERS THEN
RAISE NOTICE '@extschema@._CDB_total_relation_size(''%'', ''%'') caught error: % (%)', _schema_name, _table_name, SQLERRM, SQLSTATE;
RAISE NOTICE 'cartodb._CDB_total_relation_size(''%'', ''%'') caught error: % (%)', _schema_name, _table_name, SQLERRM, SQLSTATE;
END;
RETURN relation_size;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE;
-- Return the estimated size of user data. Used for quota checking.
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize(schema_name TEXT)
CREATE OR REPLACE FUNCTION CDB_UserDataSize(schema_name TEXT)
RETURNS bigint AS
$$
DECLARE
total_size INT8;
raster_available BOOLEAN;
raster_read_query TEXT;
BEGIN
-- Postgis 3+ might not install raster
raster_available := EXISTS (
SELECT 1
FROM pg_views
WHERE schemaname = '@postgisschema@'
AND viewname = 'raster_overviews'
);
IF raster_available THEN
raster_read_query := Format('SELECT o_table_name, r_table_name FROM @postgisschema@.raster_overviews
WHERE o_table_schema = %L AND o_table_catalog = current_database()', schema_name);
ELSE
raster_read_query := 'SELECT NULL::text AS o_table_name, NULL::text AS r_table_name';
END IF;
EXECUTE Format('
WITH raster_tables AS (
%s
SELECT o_table_name, r_table_name FROM raster_overviews
WHERE o_table_schema = schema_name AND o_table_catalog = current_database()
),
user_tables AS (
SELECT table_name FROM @extschema@._CDB_NonAnalysisTablesInSchema(%L)
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'
),
table_cat AS (
SELECT
table_name,
(
EXISTS(select * from raster_tables where o_table_name = table_name)
OR table_name SIMILAR TO @extschema@._CDB_OverviewTableDiscriminator() || ''[\w\d]*''
) AS is_overview,
EXISTS(select * from raster_tables where o_table_name = table_name) AS is_overview,
EXISTS(SELECT * FROM raster_tables WHERE r_table_name = table_name) AS is_raster
FROM user_tables
),
sizes AS (
SELECT COALESCE(INT8(SUM(@extschema@._CDB_total_relation_size(%L, table_name)))) table_size,
SELECT COALESCE(INT8(SUM(cartodb._CDB_total_relation_size(schema_name, table_name)))) table_size,
CASE
WHEN is_overview THEN 0
WHEN is_raster THEN 1
ELSE 0.5 -- Division by 2 is for not counting the_geom_webmercator
END AS multiplier FROM table_cat GROUP BY is_overview, is_raster
)
SELECT sum(table_size*multiplier)::int8 FROM sizes
', raster_read_query, schema_name, schema_name) INTO total_size;
SELECT sum(table_size*multiplier)::int8 INTO total_size FROM sizes;
IF total_size IS NOT NULL THEN
RETURN total_size;
@ -72,20 +56,20 @@ BEGIN
END IF;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE;
-- Return the estimated size of user data. Used for quota checking.
-- Implicit schema version for backwards compatibility
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize()
CREATE OR REPLACE FUNCTION CDB_UserDataSize()
RETURNS bigint AS
$$
SELECT @extschema@.CDB_UserDataSize('public');
SELECT public.CDB_UserDataSize('public');
$$
LANGUAGE 'sql' VOLATILE PARALLEL UNSAFE;
LANGUAGE 'sql' VOLATILE;
-- Triggers cannot have declared arguments: pbfact float8, qmax int8, schema_name text
CREATE OR REPLACE FUNCTION @extschema@.CDB_CheckQuota()
CREATE OR REPLACE FUNCTION CDB_CheckQuota()
RETURNS trigger AS
$$
DECLARE
@ -97,7 +81,7 @@ DECLARE
BEGIN
IF TG_NARGS = 3 THEN
schema_name := TG_ARGV[2];
IF @extschema@.schema_exists(schema_name) = false THEN
IF cartodb.schema_exists(schema_name) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
END IF;
ELSE
@ -128,7 +112,7 @@ BEGIN
RETURN NEW;
END IF;
SELECT @extschema@.CDB_UserDataSize(schema_name) INTO quota;
SELECT public.CDB_UserDataSize(schema_name) INTO quota;
IF quota > qmax THEN
RAISE EXCEPTION 'Quota exceeded by %KB', (quota-qmax)/1024;
ELSE RAISE DEBUG 'User quota in bytes: % < % (max allowed)', quota, qmax;
@ -138,16 +122,16 @@ BEGIN
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE;
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
RETURNS int8 AS
$$
DECLARE
sql text;
BEGIN
IF @extschema@.schema_exists(schema_name::text) = false THEN
IF cartodb.schema_exists(schema_name::text) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name::text;
END IF;
@ -159,14 +143,14 @@ BEGIN
return bytes;
END
$$
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE STRICT;
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(bytes int8)
CREATE OR REPLACE FUNCTION CDB_SetUserQuotaInBytes(bytes int8)
RETURNS int8 AS
$$
BEGIN
return @extschema@.CDB_SetUserQuotaInBytes('public', bytes);
return public.CDB_SetUserQuotaInBytes('public', bytes);
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE STRICT;

View File

@ -15,7 +15,7 @@
--
--
-- }{
CREATE OR REPLACE FUNCTION @extschema@.CDB_RandomTids(in_table regclass, in_nsamples integer)
CREATE OR REPLACE FUNCTION CDB_RandomTids(in_table regclass, in_nsamples integer)
RETURNS tid[]
AS $$
DECLARE
@ -64,6 +64,6 @@ BEGIN
RETURN tidlist;
END
$$ LANGUAGE 'plpgsql' STABLE STRICT PARALLEL SAFE;
$$ LANGUAGE 'plpgsql' STABLE STRICT;
-- }

View File

@ -1,6 +1,3 @@
-- In older versions of the extension, CDB_RectangleGrid had a different signature
DROP FUNCTION IF EXISTS @extschema@.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8, GEOMETRY);
--
-- Fill given extent with a rectangular coverage
--
@ -9,7 +6,7 @@ DROP FUNCTION IF EXISTS @extschema@.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8,
-- be emitted. The returned hexagons will have the same SRID
-- as this extent.
--
-- @param width Width of each rectangle
-- @param width With of each rectangle
--
-- @param height Height of each rectangle
--
@ -17,12 +14,9 @@ DROP FUNCTION IF EXISTS @extschema@.CDB_RectangleGrid(GEOMETRY, FLOAT8, FLOAT8,
-- If omitted the origin will be 0,0.
-- The parameter is checked for having the same SRID
-- as the extent.
--
--
-- @param maxcells Optional maximum number of grid cells to generate;
-- if the grid requires more cells to cover the extent
-- and exception will occur.
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_RectangleGrid(ext GEOMETRY, width FLOAT8, height FLOAT8, origin GEOMETRY DEFAULT NULL, maxcells INTEGER DEFAULT 512*512)
CREATE OR REPLACE FUNCTION CDB_RectangleGrid(ext GEOMETRY, width FLOAT8, height FLOAT8, origin GEOMETRY DEFAULT NULL)
RETURNS SETOF GEOMETRY
AS $$
DECLARE
@ -44,17 +38,17 @@ DECLARE
srid INTEGER;
BEGIN
srid := @postgisschema@.ST_SRID(ext);
srid := ST_SRID(ext);
xoff := 0;
yoff := 0;
IF origin IS NOT NULL THEN
IF @postgisschema@.ST_SRID(origin) != srid THEN
IF ST_SRID(origin) != srid THEN
RAISE EXCEPTION 'SRID mismatch between extent (%) and origin (%)', srid, ST_SRID(origin);
END IF;
xoff := @postgisschema@.ST_X(origin);
yoff := @postgisschema@.ST_Y(origin);
xoff := ST_X(origin);
yoff := ST_Y(origin);
END IF;
--RAISE DEBUG 'X offset: %', xoff;
@ -72,11 +66,11 @@ BEGIN
vstep := height;
-- Tweak horizontal start on hstep grid from origin
hstart := xoff + ceil((@postgisschema@.ST_XMin(ext)-xoff)/hstep)*hstep;
hstart := xoff + ceil((ST_XMin(ext)-xoff)/hstep)*hstep;
--RAISE DEBUG 'hstart: %', hstart;
-- Tweak vertical start on vstep grid from origin
vstart := yoff + ceil((@postgisschema@.ST_Ymin(ext)-yoff)/vstep)*vstep;
vstart := yoff + ceil((ST_Ymin(ext)-yoff)/vstep)*vstep;
--RAISE DEBUG 'vstart: %', vstart;
hend := ST_XMax(ext);
@ -85,19 +79,13 @@ BEGIN
--RAISE DEBUG 'hend: %', hend;
--RAISE DEBUG 'vend: %', vend;
If maxcells IS NOT NULL AND maxcells > 0 THEN
IF ((hend - hstart)/hstep * (vend - vstart)/vstep)::integer > maxcells THEN
RAISE EXCEPTION 'The requested grid is too big to be rendered';
END IF;
END IF;
x := hstart;
WHILE x < hend LOOP -- over X
y := vstart;
h := @postgisschema@.ST_MakeEnvelope(x-hw, y-hh, x+hw, y+hh, srid);
h := ST_MakeEnvelope(x-hw, y-hh, x+hw, y+hh, srid);
WHILE y < vend LOOP -- over Y
RETURN NEXT h;
h := @postgisschema@.ST_Translate(h, 0, vstep);
h := ST_Translate(h, 0, vstep);
y := yoff + round(((y + vstep)-yoff)/ygrd)*ygrd; -- round to grid
END LOOP;
x := xoff + round(((x + hstep)-xoff)/xgrd)*xgrd; -- round to grid
@ -105,4 +93,4 @@ BEGIN
RETURN;
END
$$ LANGUAGE 'plpgsql' IMMUTABLE PARALLEL SAFE;
$$ LANGUAGE 'plpgsql' IMMUTABLE;

View File

@ -1,153 +0,0 @@
--
-- Given a table, returns a series of queries that can be used to recreate it
-- It does not include data
--
CREATE OR REPLACE FUNCTION @extschema@.__CDB_RegenerateTable_Get_Commands(tableoid OID)
RETURNS text[]
AS $$
import subprocess
import re
query = "SELECT current_setting('statement_timeout') as t"
rv = plpy.execute(query, 1)
timeout_string = str(rv[0]['t'])
if timeout_string == "0":
timeout_string = "1min"
query = "SELECT current_database()::text as dname"
rv = plpy.execute(query, 1)
database_name_string = str(rv[0]['dname'])
query = """SELECT concat(quote_ident(nspname), '.', quote_ident(relname)) as quoted_name
FROM pg_catalog.pg_class AS c
JOIN pg_catalog.pg_namespace AS ns
ON c.relnamespace = ns.oid
WHERE c.oid = '%s'""" % (tableoid)
rv = plpy.execute(query, 1)
full_tablename_string = str(rv[0]['quoted_name'])
# NOTE: We always use -s so data is never dumped!
# That would be a security issue that we would need to deal with (and we currently do not need it)
process_parameters = ["pg_dump", "-s", "--lock-wait-timeout", timeout_string, "-t", full_tablename_string, database_name_string]
proc = subprocess.Popen(process_parameters, stdout=subprocess.PIPE, shell=False)
(out, err) = proc.communicate()
if (err or not out):
plpy.error('Could not get table properties')
line = out.decode("utf-8")
lines = line.rsplit(";\n", -1)
clean_lines = []
for i in range(0, len(lines)):
line = lines[i]
sublines = line.splitlines()
sublines = [line.rstrip() for line in sublines]
sublines = [line for line in sublines if line]
sublines = [line for line in sublines if not line.startswith('--')]
sublines = [line for line in sublines if not line.lower().startswith('set ')]
sublines = [line for line in sublines if line.lower().find('pg_catalog.set_config(') == -1]
if len(sublines):
clean_lines.append("".join(sublines))
return clean_lines
$$
LANGUAGE @@plpythonu@@ VOLATILE PARALLEL UNSAFE;
-- Returns a list of queries that can be used to regenerate the structure of a table
-- The query to create the table is not included
-- The optional parameter **ignore_cartodbfication** will remove queries related to the cartodbfication of the table
CREATE OR REPLACE FUNCTION @extschema@.CDB_GetTableQueries(tableoid OID, ignore_cartodbfication BOOL DEFAULT false)
RETURNS text[]
AS
$$
DECLARE
children INTEGER;
queries TEXT[];
BEGIN
EXECUTE FORMAT ('SELECT count(*) FROM pg_catalog.pg_inherits WHERE inhparent = %L', tableoid)
INTO children;
IF children > 0 THEN
RAISE EXCEPTION 'CDB_GetTableQueries does not support the parent of partitioned tables';
END IF;
IF NOT ignore_cartodbfication THEN
EXECUTE FORMAT('
SELECT array_agg(a)
FROM unnest(@extschema@.__CDB_RegenerateTable_Get_Commands(%L)) a
WHERE a NOT SIMILAR TO ''CREATE TABLE%%'';', tableoid) INTO queries;
ELSE
EXECUTE FORMAT('
SELECT array_agg(a)
FROM unnest(@extschema@.__CDB_RegenerateTable_Get_Commands(%L)) a
WHERE a NOT SIMILAR TO ''CREATE TABLE%%'' AND
a NOT SIMILAR TO (''%%PRIMARY KEY \(cartodb_id\)%%'') AND
a NOT SIMILAR TO (''%%cartodb_id_seq%%'') AND
a NOT SIMILAR TO (''%%track_updates%%'') AND
a NOT SIMILAR TO (''%%update_the_geom_webmercator_trigger%%'') AND
a NOT SIMILAR TO (''%%test_quota%%'') AND
a NOT SIMILAR TO (''%%test_quota_per_row%%'') AND
a NOT SIMILAR TO (''%%gist \(the_geom\)%%'') AND
a NOT SIMILAR TO (''%%gist \(the_geom_webmercator\)%%'');', tableoid) INTO queries;
END IF;
RETURN queries;
END
$$
LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;
-- Helper function to apply the result of CDB_GetTableQueries catching and discarding any exceptions
CREATE OR REPLACE FUNCTION @extschema@.CDB_ApplyQueriesSafe(queries TEXT[])
RETURNS void
AS
$$
DECLARE
i INTEGER;
BEGIN
IF array_length(queries, 1) > 0 THEN
FOR i IN 1 .. array_upper(queries, 1)
LOOP
BEGIN
EXECUTE queries[i];
EXCEPTION WHEN OTHERS THEN
CONTINUE;
END;
END LOOP;
END IF;
END
$$
LANGUAGE PLPGSQL STRICT VOLATILE PARALLEL UNSAFE;
-- Regenerates a table
CREATE OR REPLACE FUNCTION @extschema@.CDB_RegenerateTable(tableoid OID)
RETURNS void
AS
$$
DECLARE
temp_name TEXT := 'temp_' || encode(sha224(random()::text::bytea), 'hex');
table_name TEXT;
queries TEXT[] := @extschema@.__CDB_RegenerateTable_Get_Commands(tableoid);
i INTEGER;
children INTEGER;
BEGIN
EXECUTE FORMAT ('SELECT count(*) FROM pg_catalog.pg_inherits WHERE inhparent = %L', tableoid)
INTO children;
IF children > 0 THEN
RAISE EXCEPTION 'CDB_RegenerateTable does not support the parent of partitioned tables';
END IF;
EXECUTE FORMAT('SELECT concat(quote_ident(nspname), ''.'', quote_ident(relname)) as quoted_name
FROM pg_catalog.pg_class AS c
JOIN pg_catalog.pg_namespace AS ns
ON c.relnamespace = ns.oid
WHERE c.oid = %L', tableoid) INTO table_name;
EXECUTE FORMAT('CREATE TEMPORARY TABLE %s ON COMMIT DROP AS SELECT * FROM %s', temp_name, table_name);
EXECUTE FORMAT('DROP TABLE %s', table_name);
FOR i IN 1 .. array_upper(queries, 1)
LOOP
EXECUTE queries[i];
END LOOP;
EXECUTE FORMAT('INSERT INTO %s SELECT * FROM %I', table_name, temp_name);
END
$$ LANGUAGE PLPGSQL VOLATILE PARALLEL UNSAFE;

View File

@ -1,4 +1,4 @@
---- Make sure '@extschema@' is in database search path
---- Make sure 'cartodb' is in database search path
DO
$$
DECLARE
@ -8,13 +8,13 @@ BEGIN
SELECT reset_val INTO var_cur_search_path
FROM pg_settings WHERE name = 'search_path';
IF var_cur_search_path LIKE '%@extschema@%' THEN
RAISE DEBUG '"@extschema@" already in database search_path';
IF var_cur_search_path LIKE '%cartodb%' THEN
RAISE DEBUG '"cartodb" already in database search_path';
ELSE
var_cur_search_path := var_cur_search_path || ', "@extschema@"';
var_cur_search_path := var_cur_search_path || ', "cartodb"';
EXECUTE 'ALTER DATABASE ' || quote_ident(current_database()) ||
' SET search_path = ' || var_cur_search_path;
RAISE DEBUG '"@extschema@" has been added to end of database search_path';
RAISE DEBUG '"cartodb" has been added to end of database search_path';
END IF;
-- Reset search_path

View File

@ -4,50 +4,44 @@
-- @param in_array A numeric array of numbers
--
-- Returns: statistical quantity chosen
--
--
-- References: http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
--
-- Calculate kurtosis
CREATE OR REPLACE FUNCTION @extschema@.CDB_Kurtosis ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
CREATE OR REPLACE FUNCTION CDB_Kurtosis ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
DECLARE
a numeric;
c numeric;
s numeric;
k numeric;
BEGIN
SELECT AVG(e), COUNT(e)::numeric * power(stddev(e),4) INTO a, c FROM ( SELECT unnest(in_array) e ) x;
SELECT AVG(e), COUNT(e)::numeric, stddev(e) INTO a, c, s FROM ( SELECT unnest(in_array) e ) x;
IF c=0 THEN
RETURN 0;
ELSE
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;
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;
RETURN k;
END;
$$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;
-- Calculate skewness
CREATE OR REPLACE FUNCTION @extschema@.CDB_Skewness ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
CREATE OR REPLACE FUNCTION CDB_Skewness ( in_array NUMERIC[] ) RETURNS NUMERIC as $$
DECLARE
a numeric;
c numeric;
s numeric;
sk numeric;
BEGIN
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;
SELECT AVG(e), COUNT(e)::numeric, stddev(e) INTO a, c, s FROM ( SELECT unnest(in_array) e ) x;
RETURN sk;
END IF;
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;
END;
$$ language plpgsql IMMUTABLE STRICT PARALLEL SAFE;
$$ language plpgsql IMMUTABLE;

View File

@ -1,7 +1,7 @@
-- Convert string to date
--
DROP FUNCTION IF EXISTS @extschema@.CDB_StringToDate(character varying);
CREATE OR REPLACE FUNCTION @extschema@.CDB_StringToDate(input character varying)
DROP FUNCTION IF EXISTS CDB_StringToDate(character varying);
CREATE OR REPLACE FUNCTION CDB_StringToDate(input character varying)
RETURNS TIMESTAMP AS $$
DECLARE output TIMESTAMP;
BEGIN
@ -17,4 +17,4 @@ BEGIN
RETURN output;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' STABLE STRICT;

View File

@ -1,167 +0,0 @@
/*
Gets the column names of a given table.
Sample usage:
SELECT @extschema@._CDB_GetColumns('public.films');
*/
CREATE OR REPLACE FUNCTION @extschema@._CDB_GetColumns(src_table REGCLASS)
RETURNS SETOF NAME
AS $$
SELECT
a.attname as "colname"
FROM
pg_catalog.pg_attribute a
WHERE
a.attnum > 0
AND NOT a.attisdropped
AND a.attrelid = (
SELECT c.oid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.oid = src_table::oid
AND pg_catalog.pg_table_is_visible(c.oid)
)
ORDER BY a.attnum;
$$ LANGUAGE sql STABLE PARALLEL UNSAFE;
/*
Given an array of quoted column names, it generates an UPDATE SET
clause with the following form:
the_geom = changed.the_geom,
id = changed.id,
elevation = changed.elevation
Example of usage:
SELECT @extschema@.__CDB_GetUpdateSetClause('{the_geom, id, elevation}', 'changed');
*/
CREATE OR REPLACE FUNCTION @extschema@.__CDB_GetUpdateSetClause(colnames TEXT[], update_source TEXT)
RETURNS TEXT
AS $$
DECLARE
set_clause_list TEXT[];
col TEXT;
BEGIN
FOREACH col IN ARRAY colnames
LOOP
set_clause_list := array_append(set_clause_list, format('%1$s = %2$s.%1$s', col, update_source));
END lOOP;
RETURN array_to_string(set_clause_list, ', ');
END;
$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
/*
Given a prefix, generate a safe unique NAME for a temp table.
Example of usage:
SELECT @extschema@.__CDB_GenerateUniqueName('src_sync'); --> src_sync_718794_120106
*/
CREATE OR REPLACE FUNCTION @extschema@.__CDB_GenerateUniqueName(prefix TEXT)
RETURNS NAME
AS $$
SELECT format('%s_%s_%s', prefix, txid_current(), (random()*1000000)::int)::NAME;
$$ LANGUAGE sql VOLATILE PARALLEL UNSAFE;
/*
Given a table name and an array of column names,
return array of column names qualified with the table name and quoted when necessary
tablename and colnames should be properly quoted, and for this reason the type NAME is not
used for them (with quotes they could exceed the maximum identifier length)
Example of usage:
SELECT @extschema@.__CDB_QualifyColumns('t', ARRAY['a','"b-1"']); --> ARRAY['t.a','t."b-1"']
*/
CREATE OR REPLACE FUNCTION @extschema@.__CDB_QualifyColumns(tablename NAME, colnames NAME[]) RETURNS TEXT[] AS
$$
SELECT array_agg(tablename || '.' || _colname) from unnest(colnames) _colname;
$$ LANGUAGE sql IMMUTABLE PARALLEL SAFE;
/*
A Table Syncer
Assumptions:
- Both tables contain a consistent cartodb_id column
- Destination table has all columns of the source or does not exist
Sample usage:
SELECT CDB_SyncTable('radar_stations', 'public', 'syncdest');
SELECT CDB_SyncTable('test_sync_source', 'public', 'test_sync_dest', '{the_geom, the_geom_webmercator}');
*/
CREATE OR REPLACE FUNCTION @extschema@.CDB_SyncTable(src_table REGCLASS, dst_schema REGNAMESPACE, dst_table NAME, skip_cols NAME[] = '{}')
RETURNS void
AS $$
DECLARE
fq_dest_table TEXT;
colnames TEXT[];
dst_colnames TEXT;
src_colnames TEXT;
update_set_clause TEXT;
num_rows BIGINT;
err_context text;
t timestamptz;
BEGIN
-- If the destination table does not exist, just copy the source table
fq_dest_table := format('%s.%I', dst_schema, dst_table);
EXECUTE format('CREATE TABLE IF NOT EXISTS %s as TABLE %s', fq_dest_table, src_table);
GET DIAGNOSTICS num_rows = ROW_COUNT;
IF num_rows > 0 THEN
RAISE NOTICE 'INSERTED % row(s)', num_rows;
RETURN;
END IF;
skip_cols := skip_cols || '{cartodb_id}';
-- Get the list of columns from the source table, excluding skip_cols
SELECT ARRAY(SELECT quote_ident(c) FROM @extschema@._CDB_GetColumns(src_table) as c EXCEPT SELECT unnest(skip_cols)) INTO colnames;
-- Deal with deleted rows: ids in dest but not in source
t := clock_timestamp();
EXECUTE format(
'DELETE FROM %1$s _dst WHERE NOT EXISTS (SELECT * FROM %2$s _src WHERE _src.cartodb_id=_dst.cartodb_id)',
fq_dest_table, src_table);
GET DIAGNOSTICS num_rows = ROW_COUNT;
RAISE NOTICE 'DELETED % row(s)', num_rows;
RAISE DEBUG 'DELETE time (s): %', clock_timestamp() - t;
-- Deal with inserted rows: ids in source but not in dest
t := clock_timestamp();
EXECUTE format('
INSERT INTO %1$s(cartodb_id, %2$s)
SELECT cartodb_id, %2$s FROM %3$s _src WHERE NOT EXISTS (SELECT * FROM %1$s _dst WHERE _src.cartodb_id=_dst.cartodb_id)
', fq_dest_table, array_to_string(colnames, ','), src_table);
GET DIAGNOSTICS num_rows = ROW_COUNT;
RAISE NOTICE 'INSERTED % row(s)', num_rows;
RAISE DEBUG 'INSERT time (s): %', clock_timestamp() - t;
-- Deal with modified rows: ids in source and dest but different hashes
t := clock_timestamp();
update_set_clause := @extschema@.__CDB_GetUpdateSetClause(colnames, '_changed');
dst_colnames := array_to_string(@extschema@.__CDB_QualifyColumns('_dst', colnames), ',');
src_colnames := array_to_string(@extschema@.__CDB_QualifyColumns('_src', colnames), ',');
EXECUTE format('
UPDATE %1$s _update SET %2$s
FROM (
SELECT _src.* FROM %3$s _src JOIN %1$s _dst ON (_dst.cartodb_id = _src.cartodb_id)
WHERE md5(ROW(%4$s)::text) <> md5(ROW(%5$s)::text)
) _changed
WHERE _update.cartodb_id = _changed.cartodb_id;
', fq_dest_table, update_set_clause, src_table, dst_colnames, src_colnames);
GET DIAGNOSTICS num_rows = ROW_COUNT;
RAISE NOTICE 'MODIFIED % row(s)', num_rows;
RAISE DEBUG 'UPDATE time (s): %', clock_timestamp() - t;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;

View File

@ -1,5 +1,5 @@
-- Function returning indexes for a table
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableIndexes(REGCLASS)
CREATE OR REPLACE FUNCTION CDB_TableIndexes(REGCLASS)
RETURNS TABLE(index_name name, index_unique bool, index_primary bool, index_keys text array)
AS $$
@ -16,12 +16,12 @@ AS $$
JOIN pg_class
ON pg_class.oid = idx.indexrelid
WHERE pg_indexes.tablename = '' || $1 || ''
AND '' || $1 || '' IN (SELECT @extschema@.CDB_UserTables())
AND '' || $1 || '' IN (SELECT CDB_UserTables())
AND pg_class.relname=pg_indexes.indexname
;
$$ LANGUAGE SQL STABLE PARALLEL SAFE;
$$ LANGUAGE SQL;
-- This is to migrate from pre-0.2.0 version
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
GRANT EXECUTE ON FUNCTION @extschema@.CDB_TableIndexes(REGCLASS) TO public;
GRANT EXECUTE ON FUNCTION CDB_TableIndexes(REGCLASS) TO public;

View File

@ -1,18 +1,18 @@
CREATE TABLE IF NOT EXISTS
@extschema@.CDB_TableMetadata (
public.CDB_TableMetadata (
tabname regclass not null primary key,
updated_at timestamp with time zone not null default now()
);
CREATE OR REPLACE VIEW @extschema@.CDB_TableMetadata_Text AS
CREATE OR REPLACE VIEW public.CDB_TableMetadata_Text AS
SELECT FORMAT('%I.%I', n.nspname::text, c.relname::text) tabname, updated_at
FROM @extschema@.CDB_TableMetadata m JOIN pg_catalog.pg_class c ON m.tabname::oid = c.oid
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 @extschema@.CDB_TableMetadata TO public;
-- GRANT SELECT ON public.CDB_TableMetadata TO public;
--
-- Trigger logging updated_at in the CDB_TableMetadata
@ -27,45 +27,42 @@ CREATE OR REPLACE VIEW @extschema@.CDB_TableMetadata_Text AS
--
-- NOTE: _never_ attach to CDB_TableMetadata ...
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableMetadata_Trigger()
CREATE OR REPLACE FUNCTION CDB_TableMetadata_Trigger()
RETURNS trigger AS
$$
BEGIN
-- Guard against infinite loop
IF TG_RELID = '@extschema@.CDB_TableMetadata'::regclass::oid THEN
IF TG_RELID = 'public.CDB_TableMetadata'::regclass::oid THEN
RETURN NULL;
END IF;
-- Cleanup stale entries
DELETE FROM @extschema@.CDB_TableMetadata
DELETE FROM public.CDB_TableMetadata
WHERE NOT EXISTS (
SELECT oid FROM pg_class WHERE oid = tabname
);
WITH nv as (
SELECT TG_RELID as tabname, now() as t
SELECT TG_RELID as tabname, NOW() as t
), updated as (
UPDATE @extschema@.CDB_TableMetadata x SET updated_at = nv.t
UPDATE public.CDB_TableMetadata x SET updated_at = nv.t
FROM nv WHERE x.tabname = nv.tabname
RETURNING x.tabname
)
INSERT INTO @extschema@.CDB_TableMetadata SELECT nv.*
INSERT INTO public.CDB_TableMetadata SELECT nv.*
FROM nv LEFT JOIN updated USING(tabname)
WHERE updated.tabname IS NULL;
RETURN NULL;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
--
-- Trigger invalidating varnish whenever CDB_TableMetadata
-- record change.
--
CREATE OR REPLACE FUNCTION @extschema@._CDB_TableMetadata_Updated()
CREATE OR REPLACE FUNCTION _CDB_TableMetadata_Updated()
RETURNS trigger AS
$$
DECLARE
@ -96,14 +93,14 @@ BEGIN
--
-- Call the first varnish invalidation function owned
-- by a superuser found in @extschema@ or public schema
-- by a superuser found in cartodb or public schema
-- (in that order)
found := false;
FOR rec IN SELECT u.usesuper, u.usename, n.nspname, p.proname
FROM pg_proc p, pg_namespace n, pg_user u
WHERE p.proname = 'cdb_invalidate_varnish'
AND p.pronamespace = n.oid
AND n.nspname IN ('public', '@extschema@')
AND n.nspname IN ('public', 'cartodb')
AND u.usesysid = p.proowner
AND u.usesuper
ORDER BY n.nspname
@ -119,34 +116,31 @@ BEGIN
RETURN NULL;
END;
$$ LANGUAGE plpgsql
VOLATILE
PARALLEL UNSAFE
SECURITY DEFINER
SET search_path = pg_temp;
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
DROP TRIGGER IF EXISTS table_modified ON @extschema@.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 @extschema@.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
@extschema@._CDB_TableMetadata_Updated();
ON public.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
_CDB_TableMetadata_Updated();
-- similar to TOUCH(1) in unix filesystems but for table in cdb_tablemetadata
CREATE OR REPLACE FUNCTION @extschema@.CDB_TableMetadataTouch(tablename regclass)
CREATE OR REPLACE FUNCTION public.CDB_TableMetadataTouch(tablename regclass)
RETURNS void AS
$$
BEGIN
WITH upsert AS (
UPDATE @extschema@.cdb_tablemetadata
UPDATE public.cdb_tablemetadata
SET updated_at = NOW()
WHERE tabname = tablename
RETURNING *
)
INSERT INTO @extschema@.cdb_tablemetadata (tabname, updated_at)
INSERT INTO public.cdb_tablemetadata (tabname, updated_at)
SELECT tablename, NOW()
WHERE NOT EXISTS (SELECT * FROM upsert);
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
LANGUAGE 'plpgsql' VOLATILE STRICT;

View File

@ -5,19 +5,19 @@
-- for web mercator by "clipping" anything outside to the valid
-- range.
--
CREATE OR REPLACE FUNCTION @extschema@.CDB_TransformToWebmercator(geom @postgisschema@.geometry)
RETURNS @postgisschema@.geometry
CREATE OR REPLACE FUNCTION CDB_TransformToWebmercator(geom geometry)
RETURNS geometry
AS
$$
DECLARE
valid_extent @postgisschema@.GEOMETRY;
latlon_input @postgisschema@.GEOMETRY;
clipped_input @postgisschema@.GEOMETRY;
to_webmercator @postgisschema@.GEOMETRY;
ret @postgisschema@.GEOMETRY;
valid_extent GEOMETRY;
latlon_input GEOMETRY;
clipped_input GEOMETRY;
to_webmercator GEOMETRY;
ret GEOMETRY;
BEGIN
IF @postgisschema@.ST_Srid(geom) = 3857 THEN
IF ST_Srid(geom) = 3857 THEN
RETURN geom;
END IF;
@ -27,56 +27,52 @@ BEGIN
-- to -85.0511 to 85.0511 but as long as proj
-- does not complain we are happy
--
valid_extent := @postgisschema@.ST_MakeEnvelope(-180, -89, 180, 89, 4326);
valid_extent := ST_MakeEnvelope(-180, -89, 180, 89, 4326);
-- Then we transform to WGS84 latlon, which is
-- where we have known coordinates for the clipping
--
latlon_input := @postgisschema@.ST_Transform(geom, 4326);
--
latlon_input := ST_Transform(geom, 4326);
-- Don't bother clipping if the geometry boundary doesn't
-- go outside the valid extent.
IF @postgisschema@.geometry_within(latlon_input, valid_extent) THEN
BEGIN
RETURN @postgisschema@.ST_Transform(latlon_input, 3857);
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
IF latlon_input @ valid_extent THEN
RETURN ST_Transform(latlon_input, 3857);
END IF;
-- Since we're going to use ST_Intersection on input
-- we'd better ensure the input is valid
-- TODO: only do this if the first ST_Intersection fails ?
IF @postgisschema@.ST_Dimension(geom) != 0 AND
IF ST_Dimension(geom) != 0 AND
-- See http://trac.osgeo.org/postgis/ticket/1719
@postgisschema@.GeometryType(geom) != 'GEOMETRYCOLLECTION'
GeometryType(geom) != 'GEOMETRYCOLLECTION'
THEN
BEGIN
latlon_input := @postgisschema@.ST_MakeValid(latlon_input);
latlon_input := ST_MakeValid(latlon_input);
EXCEPTION
WHEN OTHERS THEN
-- See http://github.com/Vizzuality/cartodb/issues/931
RAISE WARNING 'Could not clean input geometry: %', SQLERRM;
RETURN NULL;
RETURN NULL;
END;
latlon_input := @postgisschema@.ST_CollectionExtract(latlon_input, ST_Dimension(geom)+1);
latlon_input := ST_CollectionExtract(latlon_input, ST_Dimension(geom)+1);
END IF;
-- Then we clip, trying to retain the input type
-- TODO: catch exceptions here too ?
clipped_input := @postgisschema@.ST_Intersection(latlon_input, valid_extent);
clipped_input := ST_Intersection(latlon_input, valid_extent);
-- We transform to web mercator
to_webmercator := @postgisschema@.ST_Transform(clipped_input, 3857);
to_webmercator := ST_Transform(clipped_input, 3857);
-- Finally we convert EMPTY to NULL
-- Finally we convert EMPTY to NULL
-- See https://github.com/Vizzuality/cartodb/issues/706
-- And retain "multi" status
ret := CASE WHEN @postgisschema@.ST_IsEmpty(to_webmercator) THEN NULL::@postgisschema@.geometry
WHEN @postgisschema@.GeometryType(geom) LIKE 'MULTI%' THEN @postgisschema@.ST_Multi(to_webmercator)
ret := CASE WHEN ST_IsEmpty(to_webmercator) THEN NULL::geometry
WHEN GeometryType(geom) LIKE 'MULTI%' THEN ST_Multi(to_webmercator)
ELSE to_webmercator
END;
RETURN ret;
END
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL UNSAFE;
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;

View File

@ -5,8 +5,8 @@
--
-- Currently accepted permissions are: 'public', 'private' or 'all'
--
DROP FUNCTION IF EXISTS @extschema@.CDB_UserTables(text);
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserTables(perm text DEFAULT 'all')
DROP FUNCTION IF EXISTS CDB_UserTables(text);
CREATE OR REPLACE FUNCTION CDB_UserTables(perm text DEFAULT 'all')
RETURNS SETOF name
AS $$
@ -14,15 +14,15 @@ 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', 'cdb_analysis_catalog', 'cdb_conf', 'spatial_ref_sys')
AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'topology', '@extschema@')
AND c.relname NOT IN ('cdb_tablemetadata', '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')
WHEN perm = 'all' THEN has_table_privilege(current_user, c.oid, 'SELECT') OR has_table_privilege('publicuser', c.oid, 'SELECT')
ELSE false END;
$$ LANGUAGE 'sql' STABLE PARALLEL SAFE;
$$ LANGUAGE 'sql';
-- This is to migrate from pre-0.2.0 version
-- See http://github.com/CartoDB/cartodb-postgresql/issues/36
GRANT EXECUTE ON FUNCTION @extschema@.CDB_UserTables(text) TO public;
GRANT EXECUTE ON FUNCTION CDB_UserTables(text) TO public;

View File

@ -1,10 +0,0 @@
-- Returns the cartodb username of the current PostgreSQL session
CREATE OR REPLACE FUNCTION @extschema@.CDB_Username()
RETURNS text
AS $$
SELECT @extschema@.CDB_Conf_GetConf(concat('api_keys_', session_user))->>'username';
$$ LANGUAGE SQL
STABLE
PARALLEL SAFE
SECURITY DEFINER
SET search_path = pg_temp;

View File

@ -1,12 +1,13 @@
-- {
-- Return pixel resolution at the given zoom level
-- }{
CREATE OR REPLACE FUNCTION @extschema@.CDB_XYZ_Resolution(z INTEGER)
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 6378137.0*2.0*pi() / 256.0 / power(2.0, z);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE STRICT;
SELECT 40075017.0 / 256 / power(2, z);
$$ LANGUAGE SQL IMMUTABLE STRICT;
-- }
-- {
@ -15,7 +16,7 @@ $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE STRICT;
-- SRID of the returned polygon is forceably 3857
--
-- }{
CREATE OR REPLACE FUNCTION @extschema@.CDB_XYZ_Extent(x INTEGER, y INTEGER, z INTEGER)
CREATE OR REPLACE FUNCTION CDB_XYZ_Extent(x INTEGER, y INTEGER, z INTEGER)
RETURNS GEOMETRY
AS $$
DECLARE
@ -34,7 +35,7 @@ BEGIN
-- Size of each tile in pixels (1:1 aspect ratio)
tile_size := 256;
initial_resolution := @extschema@.CDB_XYZ_Resolution(0);
initial_resolution := CDB_XYZ_Resolution(0);
--RAISE DEBUG 'Initial resolution: %', initial_resolution;
origin_shift := (initial_resolution * tile_size) / 2.0;
@ -56,7 +57,8 @@ BEGIN
--RAISE DEBUG 'ymin: %', ymin;
--RAISE DEBUG 'ymax: %', ymax;
RETURN @postgisschema@.ST_MakeEnvelope(xmin, ymin, xmax, ymax, 3857);
RETURN ST_MakeEnvelope(xmin, ymin, xmax, ymax, 3857);
END
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT PARALLEL SAFE;
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
-- }

View File

@ -1,36 +1,30 @@
-- Maximum supported zoom level
CREATE OR REPLACE FUNCTION @extschema@._CDB_MaxSupportedZoom()
RETURNS int
LANGUAGE SQL
IMMUTABLE PARALLEL SAFE
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 @extschema@.CDB_ZoomFromScale(scaleDenominator numeric)
RETURNS int
LANGUAGE SQL
IMMUTABLE PARALLEL SAFE
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
@extschema@._CDB_MaxSupportedZoom()
ELSE
CAST (
LEAST(
ROUND(LOG(2, 559082264.028/scaleDenominator)),
@extschema@._CDB_MaxSupportedZoom()
)
AS INTEGER)
END;
$$;
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,9 @@
\set ECHO none
\set QUIET on
SET client_min_messages TO error;
CREATE EXTENSION postgis;
CREATE EXTENSION @@plpythonu@@;
CREATE SCHEMA cartodb;
\i 'cartodb--unpackaged--@@VERSION@@.sql'
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE FUNCTION public.cdb_invalidate_varnish(table_name text)
RETURNS void AS $$
BEGIN
RAISE NOTICE 'cdb_invalidate_varnish(%) called', table_name;
END;
$$ LANGUAGE 'plpgsql';
\set QUIET off

View File

@ -1 +0,0 @@
\set ECHO none

View File

@ -1,20 +0,0 @@
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') t ORDER BY t;
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();

View File

@ -1,18 +0,0 @@
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

View File

@ -134,9 +134,9 @@ 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 cartodb_id, the_geom, description FROM t;
SELECT * FROM t;
SELECT CDB_CartodbfyTableCheck('t', 'check function idempotence');
SELECT cartodb_id, the_geom, description FROM t;
SELECT * FROM t;
DROP TABLE t;
-- table with existing srid-unconstrained (but type-constrained) the_geom
@ -372,24 +372,6 @@ 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;
-- Long table name could cause possible sequence rename collision #325
CREATE TABLE "wadus_table_9473e8f6-2da1-11e8-8bca-0204e4dfe4d8" ( cartodb_id serial primary key );
SELECT CDB_CartodbfyTableCheck('wadus_table_9473e8f6-2da1-11e8-8bca-0204e4dfe4d8'::REGCLASS, 'Long table name could cause sequence collision while renaming #325');
DROP TABLE "wadus_table_9473e8f6-2da1-11e8-8bca-0204e4dfe4d8";
-- TODO: table with existing custom-triggered the_geom
DROP FUNCTION CDB_CartodbfyTableCheck(regclass, text);

View File

@ -7,9 +7,9 @@ single non-geometrical column cartodbfied fine
DROP TABLE
SELECT 1
check function idempotence cartodbfied fine
1|0101000020E6100000000000000000F0BF000000000000F0BF|this is a sentence
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
check function idempotence cartodbfied fine
1|0101000020E6100000000000000000F0BF000000000000F0BF|this is a sentence
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
DROP TABLE
SELECT 1
srid-unconstrained the_geom cartodbfied fine
@ -147,11 +147,5 @@ NOTICE: Trying to recover data from _cartodb_id0 column
DROP TABLE
SET
BEGIN
DO
ROLLBACK
CREATE TABLE
Long table name could cause sequence collision while renaming #325 cartodbfied fine
DROP TABLE
DROP FUNCTION
DROP FUNCTION

View File

@ -1,6 +1,5 @@
BEGIN
CREATE TABLE
COPY 3
none||
only_com_dec|.|,
only_dot_dec|,|.

View File

@ -2,10 +2,4 @@ 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;
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;
SELECT round(unnest(CDB_EqualIntervalBins(s, 7)),7) FROM data

View File

@ -5,10 +5,3 @@
213.8571429
256.4285714
299.0000000
15.0000000
29.0000000
43.0000000
57.0000000
71.0000000
85.0000000
99.0000000

View File

@ -1,10 +0,0 @@
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;

View File

@ -1,9 +0,0 @@
SET
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 2
1
1
DROP TABLE
DROP TABLE

View File

@ -1,220 +0,0 @@
-- Setup
\set QUIET on
SET client_min_messages TO error;
\set VERBOSITY terse
SET SESSION AUTHORIZATION postgres;
CREATE EXTENSION postgres_fdw;
\set QUIET off
\echo '## List empty servers shows nothing'
SELECT '1.1', cartodb.CDB_Federated_Server_List_Servers();
\echo '## List non-existent server shows nothing'
SELECT '1.2', cartodb.CDB_Federated_Server_List_Servers(server => 'doesNotExist');
\echo '## Create and list a server works'
SELECT '1.3', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "fdw_user",
"password": "foobarino"
}
}'::jsonb);
SELECT '1.4', cartodb.CDB_Federated_Server_List_Servers();
\echo '## Create and list a second server works'
SELECT '2.1', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote2'::text, config => '{
"server": {
"dbname": "fdw_target",
"host": "localhost",
"port": @@PGPORT@@,
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
},
"credentials": {
"username": "fdw_user",
"password": "foobarino"
}
}'::jsonb);
SELECT '2.2', cartodb.CDB_Federated_Server_List_Servers();
\echo '## List server by name works'
SELECT '2.3', cartodb.CDB_Federated_Server_List_Servers(server => 'myRemote');
\echo '## Re-register a second server works'
SELECT '3.1', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote2'::text, config => '{
"server": {
"dbname": "fdw_target",
"host": "localhost",
"port": @@PGPORT@@,
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
},
"credentials": {
"username": "other_remote_user",
"password": "foobarino"
}
}'::jsonb);
SELECT '3.2', cartodb.CDB_Federated_Server_List_Servers();
\echo '## Unregister server 1 works'
SELECT '4.1', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote'::text);
SELECT '4.2', cartodb.CDB_Federated_Server_List_Servers();
\echo '## Unregistering a server that does not exist fails'
SELECT '5.1', cartodb.CDB_Federated_Server_Unregister(server => 'doesNotExist'::text);
\echo '## Unregister the second server works'
SELECT '6.1', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote2'::text);
SELECT '6.2', cartodb.CDB_Federated_Server_List_Servers();
\echo '## Create a server with NULL name fails'
SELECT '7.0', cartodb.CDB_Federated_Server_Register_PG(server => NULL::text, config => '{ "server": {}, "credentials" : {}}');
\echo '## Create a server with NULL config fails'
SELECT '7.01', cartodb.CDB_Federated_Server_Register_PG(server => 'empty'::text, config => NULL::jsonb);
\echo '## Create a server with empty config fails'
SELECT '7.1', cartodb.CDB_Federated_Server_Register_PG(server => 'empty'::text, config => '{}');
\echo '## Create a server without credentials fails'
SELECT '7.2', cartodb.CDB_Federated_Server_Register_PG(server => 'empty'::text, config => '{
"server": {
"dbname": "fdw_target",
"host": "localhost",
"port": @@PGPORT@@,
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
}
}'::jsonb);
\echo '## Create a server with empty credentials works'
SELECT '7.3', cartodb.CDB_Federated_Server_Register_PG(server => 'empty'::text, config => '{
"server": {
"dbname": "fdw_target",
"host": "localhost",
"port": @@PGPORT@@,
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
},
"credentials": { }
}'::jsonb);
SELECT '7.4', cartodb.CDB_Federated_Server_List_Servers();
SELECT '7.5', cartodb.CDB_Federated_Server_Unregister(server => 'empty'::text);
\echo '## Create a server without options fails'
SELECT '7.6', cartodb.CDB_Federated_Server_Register_PG(server => 'empty'::text, config => '{
"credentials": {
"username": "other_remote_user",
"password": "foobarino"
}
}'::jsonb);
\echo '## Create a server with special characters works'
SELECT '8.1', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote" or''not'::text, config => '{
"server": {
"dbname": "fdw target",
"host": "localhost",
"port": @@PGPORT@@,
"extensions": "postgis",
"updatable": "false",
"use_remote_estimate": "true",
"fetch_size": "1000"
},
"credentials": {
"username": "fdw user",
"password": "foo barino"
}
}'::jsonb);
SELECT '8.2', cartodb.CDB_Federated_Server_List_Servers();
SELECT '8.3', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote" or''not'::text);
-- Test permissions
\set QUIET on
CREATE ROLE cdb_fs_tester LOGIN PASSWORD 'cdb_fs_passwd';
GRANT CONNECT ON DATABASE contrib_regression TO cdb_fs_tester;
\set QUIET off
SELECT '9.1', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote3'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "fdw_user",
"password": "foobarino"
}
}'::jsonb);
\c contrib_regression cdb_fs_tester
\echo '## All users are able to list servers'
SELECT '9.2', cartodb.CDB_Federated_Server_List_Servers();
\echo '## Only superadmins can create servers'
SELECT '9.3', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote4'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "fdw_user",
"password": "foobarino"
}
}'::jsonb);
\c contrib_regression postgres
\echo '## Granting access to a user works'
SELECT '9.5', cartodb.CDB_Federated_Server_Grant_Access(server => 'myRemote3', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT '9.55', cartodb.CDB_Federated_Server_List_Servers();
\c contrib_regression postgres
SELECT '9.6', cartodb.CDB_Federated_Server_Grant_Access(server => 'does not exist', db_role => 'cdb_fs_tester'::name);
SELECT '9.7', cartodb.CDB_Federated_Server_Grant_Access(server => 'myRemote3', db_role => 'does not exist'::name);
\echo '## Granting access again raises a notice'
SELECT '9.8', cartodb.CDB_Federated_Server_Grant_Access(server => 'myRemote3', db_role => 'cdb_fs_tester'::name);
\echo '## Revoking access to a user works'
SELECT '9.9', cartodb.CDB_Federated_Server_Revoke_Access(server => 'myRemote3', db_role => 'cdb_fs_tester'::name);
SELECT '9.10', cartodb.CDB_Federated_Server_Grant_Access(server => 'myRemote3', db_role => 'cdb_fs_tester'::name);
\echo '## Unregistering a server with active grants works'
SELECT '9.11', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote3'::text);
\echo '## A user with granted access can not drop a server'
SELECT '10.1', cartodb.CDB_Federated_Server_Register_PG(server => 'myRemote4'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "fdw_user",
"password": "foobarino"
}
}'::jsonb);
SELECT '10.2', cartodb.CDB_Federated_Server_Grant_Access(server => 'myRemote4', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT '10.3', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote4'::text);
\c contrib_regression postgres
SELECT '10.4', cartodb.CDB_Federated_Server_Unregister(server => 'myRemote4'::text);
-- Cleanup
\set QUIET on
REVOKE CONNECT ON DATABASE contrib_regression FROM cdb_fs_tester;
DROP ROLE cdb_fs_tester;
DROP EXTENSION postgres_fdw;
\set QUIET off

View File

@ -1,126 +0,0 @@
-- ===================================================================
-- create FDW objects
-- ===================================================================
\set QUIET on
SET client_min_messages TO error;
\set VERBOSITY terse
CREATE EXTENSION postgres_fdw;
CREATE ROLE cdb_fs_tester LOGIN PASSWORD 'cdb_fs_passwd';
GRANT CONNECT ON DATABASE contrib_regression TO cdb_fs_tester;
-- Create database to be used as remote
CREATE DATABASE cdb_fs_tester OWNER cdb_fs_tester;
SELECT 'C1', cartodb.CDB_Federated_Server_Register_PG(server => 'loopback'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "cdb_fs_tester",
"password": "cdb_fs_passwd"
}
}'::jsonb);
SELECT 'C2', cartodb.CDB_Federated_Server_Register_PG(server => 'wrong-port'::text, config => '{
"server": {
"host": "localhost",
"port": "12345"
},
"credentials": {
"username": "cdb_fs_tester",
"password": "cdb_fs_passwd"
}
}'::jsonb);
SELECT 'C3', cartodb.CDB_Federated_Server_Register_PG(server => 'loopback-no-port'::text, config => '{
"server": {
"host": "localhost"
},
"credentials": {
"username": "cdb_fs_tester",
"password": "cdb_fs_passwd"
}
}'::jsonb);
\c cdb_fs_tester postgres
CREATE EXTENSION postgis;
\c contrib_regression postgres
\set QUIET off
-- ===================================================================
-- Test server diagnostics function(s)
-- ===================================================================
\echo '%% It raises an error if the server does not exist'
SELECT '1.1', cartodb.CDB_Federated_Server_Diagnostics(server => 'doesNotExist');
\echo '%% It returns a jsonb object'
SELECT '1.2', pg_typeof(cartodb.CDB_Federated_Server_Diagnostics(server => 'loopback'));
\echo '%% It returns the server version'
SELECT '1.3', cartodb.CDB_Federated_Server_Diagnostics(server => 'loopback') @> format('{"server_version": "%s"}', setting)::jsonb
FROM pg_settings WHERE name = 'server_version';
\echo '%% It returns the postgis version'
SELECT '1.4', cartodb.CDB_Federated_Server_Diagnostics(server => 'loopback') @> format('{"postgis_version": "%s"}', extversion)::jsonb
FROM pg_extension WHERE extname = 'postgis';
\echo '%% It returns null as the postgis version if it is not installed'
\set QUIET on
\c cdb_fs_tester postgres
DROP EXTENSION postgis;
\c contrib_regression postgres
\set QUIET off
SELECT '1.5', cartodb.CDB_Federated_Server_Diagnostics(server => 'loopback') @> '{"postgis_version": null}'::jsonb;
\echo '%% It returns the remote server options'
SELECT '1.6', cartodb.CDB_Federated_Server_Diagnostics(server => 'loopback') @> '{"server_options": {"host": "localhost", "port": "@@PGPORT@@", "updatable": "false", "extensions": "postgis", "fetch_size": "1000", "use_remote_estimate": "true"}}'::jsonb;
\echo '%% It returns network latency stats to the remote server: min <= avg <= max'
WITH latency AS (
SELECT CDB_Federated_Server_Diagnostics('loopback')->'server_latency_ms' ms
) SELECT '2.1', (latency.ms->'min')::text::float <= (latency.ms->'avg')::text::float, (latency.ms->'avg')::text::float <= (latency.ms->'max')::text::float
FROM latency;
\echo '%% Latency stats: 0 <= min <= max <= 1000 ms (local connection)'
WITH latency AS (
SELECT CDB_Federated_Server_Diagnostics('loopback')->'server_latency_ms' ms
) SELECT '2.2', 0.0 <= (latency.ms->'min')::text::float, (latency.ms->'max')::text::float <= 1000.0
FROM latency;
\echo '%% Latency stats: stdev > 0'
WITH latency AS (
SELECT CDB_Federated_Server_Diagnostics('loopback')->'server_latency_ms' ms
) SELECT '2.3', (latency.ms->'stdev')::text::float >= 0.0
FROM latency;
\echo '%% It raises an error if the wrong port is provided'
SELECT '3.0', cartodb.CDB_Federated_Server_Diagnostics(server => 'wrong-port');
-- Disabled: It's not compatible with Travis since the target database (self) might be in a different port
-- \echo '%% Latency stats: can get them on default PG port 5432 when not provided'
-- WITH latency AS (
-- SELECT CDB_Federated_Server_Diagnostics('loopback-no-port')->'server_latency_ms' ms
-- ) SELECT '2.4', 0.0 <= (latency.ms->'min')::text::float, (latency.ms->'max')::text::float <= 1000.0
-- FROM latency;
-- ===================================================================
-- Cleanup
-- ===================================================================
\set QUIET on
SELECT 'D1', cartodb.CDB_Federated_Server_Unregister(server => 'loopback'::text);
SELECT 'D2', cartodb.CDB_Federated_Server_Unregister(server => 'wrong-port'::text);
SELECT 'D3', cartodb.CDB_Federated_Server_Unregister(server => 'loopback-no-port'::text);
-- Reconnect, using a new session in order to close FDW connections
\connect
DROP DATABASE cdb_fs_tester;
-- Drop role
REVOKE CONNECT ON DATABASE contrib_regression FROM cdb_fs_tester;
DROP ROLE cdb_fs_tester;
DROP EXTENSION postgres_fdw;
\set QUIET off

View File

@ -1,26 +0,0 @@
C1|
C2|
C3|
%% It raises an error if the server does not exist
ERROR: Server "doesNotExist" does not exist
%% It returns a jsonb object
1.2|jsonb
%% It returns the server version
1.3|t
%% It returns the postgis version
1.4|t
%% It returns null as the postgis version if it is not installed
1.5|t
%% It returns the remote server options
1.6|t
%% It returns network latency stats to the remote server: min <= avg <= max
2.1|t|t
%% Latency stats: 0 <= min <= max <= 1000 ms (local connection)
2.2|t|t
%% Latency stats: stdev > 0
2.3|t
%% It raises an error if the wrong port is provided
ERROR: could not connect to server "cdb_fs_wrong-port"
D1|
D2|
D3|

View File

@ -1,319 +0,0 @@
-- ===================================================================
-- create FDW objects
-- ===================================================================
\set QUIET on
SET client_min_messages TO error;
\set VERBOSITY terse
CREATE EXTENSION postgres_fdw;
CREATE ROLE cdb_fs_tester LOGIN PASSWORD 'cdb_fs_passwd';
GRANT CONNECT ON DATABASE contrib_regression TO cdb_fs_tester;
CREATE ROLE cdb_fs_tester2 LOGIN PASSWORD 'cdb_fs_passwd2';
GRANT CONNECT ON DATABASE contrib_regression TO cdb_fs_tester2;
-- Create database to be used as remote
CREATE DATABASE cdb_fs_tester OWNER cdb_fs_tester;
SELECT 'C1', cartodb.CDB_Federated_Server_Register_PG(server => 'loopback'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "cdb_fs_tester",
"password": "cdb_fs_passwd"
}
}'::jsonb);
SELECT 'C2', cartodb.CDB_Federated_Server_Register_PG(server => 'loopback2'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "cdb_fs_tester",
"password": "cdb_fs_passwd"
}
}'::jsonb);
-- ===================================================================
-- Setup 1
-- ===================================================================
\c cdb_fs_tester cdb_fs_tester
CREATE TYPE user_enum AS ENUM ('foo', 'bar', 'buz');
CREATE SCHEMA "S 1";
CREATE TABLE "S 1"."T 1" (
"C 1" int NOT NULL,
c2 int NOT NULL,
c3 text,
c4 timestamptz,
c5 timestamp,
c6 varchar(10),
c7 char(10),
c8 user_enum,
CONSTRAINT t1_pkey PRIMARY KEY ("C 1")
);
CREATE TABLE "S 1"."T 2" (
c1 int NOT NULL,
c2 text,
CONSTRAINT t2_pkey PRIMARY KEY (c1)
);
CREATE TABLE "S 1"."T 3" (
c1 int NOT NULL,
c2 int NOT NULL,
c3 text,
CONSTRAINT t3_pkey PRIMARY KEY (c1)
);
CREATE TABLE "S 1"."T 4" (
c1 int NOT NULL,
c2 int NOT NULL,
c3 text,
CONSTRAINT t4_pkey PRIMARY KEY (c1)
);
-- Disable autovacuum for these tables to avoid unexpected effects of that
ALTER TABLE "S 1"."T 1" SET (autovacuum_enabled = 'false');
ALTER TABLE "S 1"."T 2" SET (autovacuum_enabled = 'false');
ALTER TABLE "S 1"."T 3" SET (autovacuum_enabled = 'false');
ALTER TABLE "S 1"."T 4" SET (autovacuum_enabled = 'false');
\c contrib_regression postgres
SET client_min_messages TO notice;
\set VERBOSITY terse
\set QUIET off
-- ===================================================================
-- Test listing remote schemas
-- ===================================================================
\echo '## Test listing of remote schemas without permissions before the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback');
\c contrib_regression postgres
\echo '## Test listing of remote schemas (sunny day)'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback');
\echo '## Test listing of remote schemas without permissions after the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback');
\c contrib_regression postgres
\echo '## Test listing of remote schemas with permissions (sunny day)'
SELECT cartodb.CDB_Federated_Server_Grant_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback');
\c contrib_regression postgres
\echo '## Test listing of remote schemas without permissions after revoking access (rainy day)'
SELECT cartodb.CDB_Federated_Server_Revoke_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback');
\c contrib_regression postgres
\echo '## Test listing of remote schemas (rainy day): Server does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'Does Not Exist');
-- ===================================================================
-- Test listing remote tables
-- ===================================================================
\echo '## Test listing of remote tables without permissions before the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'S 1');
\c contrib_regression postgres
\echo '## Test listing of remote tables (sunny day)'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'S 1');
\echo '## Test listing of remote tables without permissions after the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'S 1');
\c contrib_regression postgres
\echo '## Test listing of remote tables with permissions (sunny day)'
SELECT cartodb.CDB_Federated_Server_Grant_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'S 1');
\c contrib_regression postgres
\echo '## Test listing of remote tables without permissions after revoking access (rainy day)'
SELECT cartodb.CDB_Federated_Server_Revoke_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'S 1');
\c contrib_regression postgres
\echo '## Test listing of remote tables (rainy day): Server does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'Does Not Exist', remote_schema => 'S 1');
\echo '## Test listing of remote tables (rainy day): Remote schema does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback', remote_schema => 'Does Not Exist');
-- ===================================================================
-- Test listing remote columns
-- ===================================================================
\echo '## Test listing of remote columns without permissions before the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 1');
\c contrib_regression postgres
\echo '## Test listing of remote columns (sunny day)'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 1');
\echo '## Test listing of remote columns without permissions after the first instantiation (rainy day)'
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 1');
\c contrib_regression postgres
\echo '## Test listing of remote columns with permissions (sunny day)'
SELECT cartodb.CDB_Federated_Server_Grant_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 1');
\c contrib_regression postgres
\echo '## Test listing of remote columns without permissions after revoking access (rainy day)'
SELECT cartodb.CDB_Federated_Server_Revoke_Access(server => 'loopback', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 1');
\c contrib_regression postgres
\echo '## Test listing of remote columns (rainy day): Server does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'Does Not Exist', remote_schema => 'S 1', remote_table => 'T 1');
\echo '## Test listing of remote columns (rainy day): Remote schema does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'Does Not Exist', remote_table => 'T 1');
\echo '## Test listing of remote columns (rainy day): Remote table does not exist'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'Does Not Exist');
\echo '## Test listing of remote columns (rainy day): Remote table is NULL'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => NULL::text);
-- ===================================================================
-- Test that using a different user to list tables and dropping it
-- does not break the server: We use loopback2 as it's in a clean state
-- ===================================================================
\echo '## Test listing of remote objects with permissions (sunny day)'
SELECT cartodb.CDB_Federated_Server_Grant_Access(server => 'loopback2', db_role => 'cdb_fs_tester2'::name);
\c contrib_regression cdb_fs_tester2
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback2');
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback2', remote_schema => 'S 1');
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback2', remote_schema => 'S 1', remote_table => 'T 1');
\c contrib_regression postgres
\echo '## Test that dropping the granted user works fine (sunny day)'
REVOKE CONNECT ON DATABASE contrib_regression FROM cdb_fs_tester2;
DROP ROLE cdb_fs_tester2;
\echo '## Test listing of remote objects with other user still works (sunny day)'
SELECT cartodb.CDB_Federated_Server_Grant_Access(server => 'loopback2', db_role => 'cdb_fs_tester'::name);
\c contrib_regression cdb_fs_tester
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback2');
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Tables(server => 'loopback2', remote_schema => 'S 1');
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback2', remote_schema => 'S 1', remote_table => 'T 1');
-- ===================================================================
-- Cleanup 1
-- ===================================================================
\set QUIET on
\c cdb_fs_tester cdb_fs_tester
DROP TABLE "S 1". "T 1";
DROP TABLE "S 1". "T 2";
DROP TABLE "S 1". "T 3";
DROP TABLE "S 1". "T 4";
DROP SCHEMA "S 1";
DROP TYPE user_enum;
-- ===================================================================
-- Setup 2: Using Postgis too
-- ===================================================================
\c cdb_fs_tester postgres
CREATE EXTENSION postgis;
\c cdb_fs_tester cdb_fs_tester
CREATE SCHEMA "S 1";
CREATE TABLE "S 1"."T 5" (
geom geometry(Geometry,4326),
geom_wm geometry(GeometryZ,3857),
geo_nosrid geometry,
geog geography
);
\c contrib_regression postgres
SET client_min_messages TO notice;
\set VERBOSITY terse
\set QUIET off
-- ===================================================================
-- Test the listing functions
-- ===================================================================
\echo '## Test listing of remote geometry columns (sunny day)'
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 5');
\echo '## Test listing of remote geometry columns (sunny day) - Rerun'
-- Rerun should be ok
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Columns(server => 'loopback', remote_schema => 'S 1', remote_table => 'T 5');
-- ===================================================================
-- Test invalid password
-- ===================================================================
\echo '## Check error message with invalid password (rainy day)'
SELECT cartodb.CDB_Federated_Server_Register_PG(server => 'loopback_invalid'::text, config => '{
"server": {
"host": "localhost",
"port": @@PGPORT@@
},
"credentials": {
"username": "cdb_fs_tester",
"password": "wrong password"
}
}'::jsonb);
SELECT * FROM cartodb.CDB_Federated_Server_List_Remote_Schemas(server => 'loopback_invalid');
SELECT cartodb.CDB_Federated_Server_Unregister(server => 'loopback_invalid'::text);
-- ===================================================================
-- Cleanup 2
-- ===================================================================
\set QUIET on
\c cdb_fs_tester cdb_fs_tester
DROP TABLE "S 1". "T 5";
DROP SCHEMA "S 1";
\c contrib_regression postgres
\set QUIET on
SET client_min_messages TO error;
\set VERBOSITY terse
SELECT 'D1', cartodb.CDB_Federated_Server_Unregister(server => 'loopback'::text);
SELECT 'D2', cartodb.CDB_Federated_Server_Unregister(server => 'loopback2'::text);
DROP DATABASE cdb_fs_tester;
-- Drop role
REVOKE CONNECT ON DATABASE contrib_regression FROM cdb_fs_tester;
DROP ROLE cdb_fs_tester;
DROP EXTENSION postgres_fdw;
\set QUIET off

View File

@ -1,162 +0,0 @@
C1|
C2|
## Test listing of remote schemas without permissions before the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote schemas (sunny day)
S 1
information_schema
public
## Test listing of remote schemas without permissions after the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote schemas with permissions (sunny day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
S 1
information_schema
public
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote schemas without permissions after revoking access (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote schemas (rainy day): Server does not exist
ERROR: Server "Does Not Exist" does not exist
## Test listing of remote tables without permissions before the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote tables (sunny day)
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback"
f|T 1|||||[{"Name" : "C 1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}, {"Name" : "c4", "Type" : "timestamp with time zone"}, {"Name" : "c5", "Type" : "timestamp without time zone"}, {"Name" : "c6", "Type" : "character varying"}, {"Name" : "c7", "Type" : "character"}, {"Name" : "c8", "Type" : "USER-DEFINED"}]
f|T 2|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "text"}]
f|T 3|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
f|T 4|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
## Test listing of remote tables without permissions after the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote tables with permissions (sunny day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback"
f|T 1|||||[{"Name" : "C 1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}, {"Name" : "c4", "Type" : "timestamp with time zone"}, {"Name" : "c5", "Type" : "timestamp without time zone"}, {"Name" : "c6", "Type" : "character varying"}, {"Name" : "c7", "Type" : "character"}, {"Name" : "c8", "Type" : "USER-DEFINED"}]
f|T 2|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "text"}]
f|T 3|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
f|T 4|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote tables without permissions after revoking access (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote tables (rainy day): Server does not exist
ERROR: Server "Does Not Exist" does not exist
## Test listing of remote tables (rainy day): Remote schema does not exist
## Test listing of remote columns without permissions before the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote columns (sunny day)
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback"
C 1|integer
c2|integer
c3|text
c4|timestamp with time zone
c5|timestamp without time zone
c6|character varying
c7|character
c8|USER-DEFINED
## Test listing of remote columns without permissions after the first instantiation (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote columns with permissions (sunny day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback"
C 1|integer
c2|integer
c3|text
c4|timestamp with time zone
c5|timestamp without time zone
c6|character varying
c7|character
c8|USER-DEFINED
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote columns without permissions after revoking access (rainy day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
ERROR: Not enough permissions to access the server "loopback"
You are now connected to database "contrib_regression" as user "postgres".
## Test listing of remote columns (rainy day): Server does not exist
ERROR: Server "Does Not Exist" does not exist
## Test listing of remote columns (rainy day): Remote schema does not exist
## Test listing of remote columns (rainy day): Remote table does not exist
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback"
## Test listing of remote columns (rainy day): Remote table is NULL
ERROR: Remote table name cannot be NULL
## Test listing of remote objects with permissions (sunny day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester2".
S 1
information_schema
public
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback2"
f|T 1|||||[{"Name" : "C 1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}, {"Name" : "c4", "Type" : "timestamp with time zone"}, {"Name" : "c5", "Type" : "timestamp without time zone"}, {"Name" : "c6", "Type" : "character varying"}, {"Name" : "c7", "Type" : "character"}, {"Name" : "c8", "Type" : "USER-DEFINED"}]
f|T 2|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "text"}]
f|T 3|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
f|T 4|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback2"
C 1|integer
c2|integer
c3|text
c4|timestamp with time zone
c5|timestamp without time zone
c6|character varying
c7|character
c8|USER-DEFINED
You are now connected to database "contrib_regression" as user "postgres".
## Test that dropping the granted user works fine (sunny day)
REVOKE
DROP ROLE
## Test listing of remote objects with other user still works (sunny day)
You are now connected to database "contrib_regression" as user "cdb_fs_tester".
S 1
information_schema
public
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback2"
f|T 1|||||[{"Name" : "C 1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}, {"Name" : "c4", "Type" : "timestamp with time zone"}, {"Name" : "c5", "Type" : "timestamp without time zone"}, {"Name" : "c6", "Type" : "character varying"}, {"Name" : "c7", "Type" : "character"}, {"Name" : "c8", "Type" : "USER-DEFINED"}]
f|T 2|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "text"}]
f|T 3|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
f|T 4|||||[{"Name" : "c1", "Type" : "integer"}, {"Name" : "c2", "Type" : "integer"}, {"Name" : "c3", "Type" : "text"}]
INFO: Could not find Postgis installation in the remote "public" schema in server "loopback2"
C 1|integer
c2|integer
c3|text
c4|timestamp with time zone
c5|timestamp without time zone
c6|character varying
c7|character
c8|USER-DEFINED
## Test listing of remote geometry columns (sunny day)
geo_nosrid|GEOMETRY,0
geog|Geometry,0
geom|GEOMETRY,4326
geom_wm|GEOMETRY,3857
## Test listing of remote geometry columns (sunny day) - Rerun
geo_nosrid|GEOMETRY,0
geog|Geometry,0
geom|GEOMETRY,4326
geom_wm|GEOMETRY,3857
## Check error message with invalid password (rainy day)
ERROR: could not connect to server "cdb_fs_loopback_invalid"
D1|
D2|

Some files were not shown because too many files have changed in this diff Show More