Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aff7ae3e2e | ||
|
|
01757a1b6d | ||
|
|
f946bfe4fe | ||
|
|
a098713bfa | ||
|
|
217ff4ffb9 | ||
|
|
a0bb7b1b03 | ||
|
|
4074173c05 | ||
|
|
603b1ceed8 | ||
|
|
0caf6c50dd | ||
|
|
224b2ce395 | ||
|
|
70eeab5748 | ||
|
|
06c05a1d67 | ||
|
|
6567d5441b | ||
|
|
e10d7c3a27 | ||
|
|
b89a752548 | ||
|
|
90fa45b59d | ||
|
|
eb48e26eec | ||
|
|
85e1b92199 | ||
|
|
2dc6ec618f | ||
|
|
3127b9d398 | ||
|
|
91158fafb0 | ||
|
|
16cf70bb4a | ||
|
|
6ea0013343 | ||
|
|
80bd7f8f10 | ||
|
|
bb061981ad | ||
|
|
e289b4725f | ||
|
|
0b8bada553 | ||
|
|
7b48c2765e | ||
|
|
3d0f580fc2 | ||
|
|
d495bd45ba | ||
|
|
f4b51807a1 | ||
|
|
33b4f3718b | ||
|
|
2e186c8565 | ||
|
|
5bc725c8ab | ||
|
|
73906b60da | ||
|
|
03ff0365fd | ||
|
|
76a2cb9132 | ||
|
|
90c16fdb13 | ||
|
|
a80664e72a | ||
|
|
7e2193d3db | ||
|
|
a18cbeb2cd | ||
|
|
0d5f83b3c4 | ||
|
|
7559257d49 | ||
|
|
1198454046 | ||
|
|
32307ceef0 |
19
Makefile
19
Makefile
@@ -1,7 +1,7 @@
|
||||
# cartodb/Makefile
|
||||
|
||||
EXTENSION = cartodb
|
||||
EXTVERSION = 0.14.0
|
||||
EXTVERSION = 0.14.4
|
||||
|
||||
SED = sed
|
||||
|
||||
@@ -60,6 +60,10 @@ UPGRADABLE = \
|
||||
0.13.0 \
|
||||
0.13.1 \
|
||||
0.14.0 \
|
||||
0.14.1 \
|
||||
0.14.2 \
|
||||
0.14.3 \
|
||||
0.14.4 \
|
||||
$(EXTVERSION)dev \
|
||||
$(EXTVERSION)next \
|
||||
$(END)
|
||||
@@ -113,6 +117,9 @@ $(EXTENSION).control: $(EXTENSION).control.in Makefile
|
||||
cartodb_version.sql: cartodb_version.sql.in Makefile $(GITDIR)/index
|
||||
$(SED) -e 's/@@VERSION@@/$(EXTVERSION)/' $< > $@
|
||||
|
||||
# Needed for consistent `echo` results with backslashes
|
||||
SHELL = bash
|
||||
|
||||
legacy_regress: $(REGRESS_OLD) Makefile
|
||||
mkdir -p sql/test/
|
||||
mkdir -p expected/test/
|
||||
@@ -120,14 +127,14 @@ legacy_regress: $(REGRESS_OLD) Makefile
|
||||
for f in $(REGRESS_OLD); do \
|
||||
tn=`basename $${f} .sql`; \
|
||||
of=sql/test/$${tn}.sql; \
|
||||
echo '\\set ECHO none' > $${of}; \
|
||||
echo '\\a' >> $${of}; \
|
||||
echo '\\t' >> $${of}; \
|
||||
echo '\\set QUIET off' >> $${of}; \
|
||||
echo '\set ECHO none' > $${of}; \
|
||||
echo '\a' >> $${of}; \
|
||||
echo '\t' >> $${of}; \
|
||||
echo '\set QUIET off' >> $${of}; \
|
||||
cat $${f} | \
|
||||
$(SED) -e 's/public\./cartodb./g' >> $${of}; \
|
||||
exp=expected/test/$${tn}.out; \
|
||||
echo '\\set ECHO none' > $${exp}; \
|
||||
echo '\set ECHO none' > $${exp}; \
|
||||
cat test/$${tn}_expect >> $${exp}; \
|
||||
done
|
||||
|
||||
|
||||
17
NEWS.md
17
NEWS.md
@@ -1,3 +1,20 @@
|
||||
0.14.4 (2016-03-29)
|
||||
* Fix creating overviews for tables with boolean columns
|
||||
[#214](https://github.com/CartoDB/cartodb-postgresql/pull/214)
|
||||
* Fix tests for some systems [#215](https://github.com/CartoDB/cartodb-postgresql/pull/215)
|
||||
|
||||
0.14.3 (2016-03-17)
|
||||
-------------------
|
||||
* Fix for `cartodb_id` bigint casting hardcoded in 0.14.2 to support `cartodb_id` text columns [#210](https://github.com/CartoDB/cartodb-postgresql/pull/210)
|
||||
|
||||
0.14.2 (2016-03-15)
|
||||
-------------------
|
||||
* Support text `cartodb_id` columns in `_CDB_Has_Usable_Primary_ID` [#202](https://github.com/CartoDB/cartodb-postgresql/pull/202)
|
||||
|
||||
0.14.1 (2016-03-07)
|
||||
-------------------
|
||||
* Fully qualify table names in cache cdb_invalidate_varnish calls [#198](https://github.com/CartoDB/cartodb-postgresql/issues/198)
|
||||
|
||||
0.14.0 (2016-02-14)
|
||||
-------------------
|
||||
* Add CDB_ForeignTable.sql to support FDW's [#199](https://github.com/CartoDB/cartodb-postgresql/pull/199)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cartodb-postgresql
|
||||
==================
|
||||
|
||||
[]
|
||||
[]
|
||||
(http://travis-ci.org/CartoDB/cartodb-postgresql)
|
||||
|
||||
PostgreSQL extension for CartoDB
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
A "cartodb" user table is a table with a well-known set of fields and a well-known set of triggers attached on.
|
||||
|
||||
The fields are:
|
||||
|
||||
- `cartodb_id`, a numerical primary key of serial type
|
||||
- `created_at`, timestamp with timezone not null default now()
|
||||
- `updated_at`, timestamp with timezone not null default now()
|
||||
- `the_geom`, geometry, GiST indexed, constrained (see below)
|
||||
- `the_geom_webmercator`, geometry, GiST indexed, constrained (see below)
|
||||
|
||||
The values of "the_geom" and "the_geom_webmercator" must match these constraints:
|
||||
|
||||
- Only POINT, MULTILINE, MULTIPOLYGON types ? Maybe UNCONSTRAINED
|
||||
- Only 2 dimensions ? Maybe UNCONSTRAINED
|
||||
- SRID=4326 for the_geom and SRID=3857 for the_geom_webmercator
|
||||
|
||||
The triggers are:
|
||||
|
||||
- `track_updates` after modifying statement updates cdb_tablemetadata
|
||||
- `test_quota` before changing statement to forbid if overquota
|
||||
- `test_quota_per_row` before changing row to forbod if overquota (checked on a probabilistic basis)
|
||||
- `update_the_geom_webmercator` before insert or update row to maintain the_geom_webmercator
|
||||
- `update_updated_at_trigger` before update row to maintain updated_at
|
||||
|
||||
Some conversions will be attempted to perform upon cartodbfication when certain fields appear:
|
||||
|
||||
- `cartodb_id`: If found type TEXT will be attempted to cast
|
||||
- `created_at`: If found type TEXT will be attempted to cast
|
||||
- `updated_at`: If found type TEXT will be attempted to cast
|
||||
68
doc/CartoDB-user-table.rst
Normal file
68
doc/CartoDB-user-table.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
CartoDB User Table
|
||||
==================
|
||||
|
||||
Introduction
|
||||
----------
|
||||
A CartoDB user table is a table with a well-known set of columns and a well-known set of triggers attached on.
|
||||
|
||||
Columns
|
||||
----------
|
||||
The required columns of a CartoDB table are:
|
||||
|
||||
- ``cartodb_id``
|
||||
- This column will be used as the primary key of the table and it has a sequence as default value
|
||||
- Its values must be integer, non-zero, non-null and unique
|
||||
- B-Tree indexed
|
||||
- ``the_geom``
|
||||
- This column stores the main geometric features of a table
|
||||
- The type of the column in the Postgres database is ``geometry(Geometry,4326)```
|
||||
- GiST indexed
|
||||
- geometry, GiST indexed, constrained (see below)
|
||||
- ``the_geom_webmercator``
|
||||
- This column stores the geometries used for rendering purposes
|
||||
- The type of the column in the Postgres database is ``geometry(Geometry,3857)``
|
||||
- GiST indexed
|
||||
- This column is automatically updated by the system when the ``the_geom`` column is updated or when there is an insertion of a new row into the table (See triggers below)
|
||||
|
||||
The values of ``the_geom`` and ``the_geom_webmercator`` must be two-dimensional Points, MultiLineStrings or MultiPolygons. Different geometric types in a CartoDB table are not supported.
|
||||
|
||||
Described table example
|
||||
^^^^^^^^^^
|
||||
::
|
||||
|
||||
Column | Type | Modifiers
|
||||
----------------------+-------------------------+--------------------------------------------------------
|
||||
cartodb_id | bigint | not null default nextval('t_cartodb_id_seq'::regclass)
|
||||
the_geom | geometry(Geometry,4326) |
|
||||
the_geom_webmercator | geometry(Geometry,3857) |
|
||||
Indexes:
|
||||
"table_name_pkey" PRIMARY KEY, btree (cartodb_id)
|
||||
"table_name_the_geom_idx" gist (the_geom)
|
||||
"table_name_the_geom_webmercator_idx" gist (the_geom_webmercator)
|
||||
|
||||
Triggers
|
||||
----------
|
||||
The triggers generated in each CartoDB table are:
|
||||
|
||||
- ``track_updates`` after modifying statement updates ``cdb_tablemetadata``
|
||||
- ``test_quota`` before changing statement to forbid if overquota
|
||||
- ``test_quota_per_row`` before insert ot update row to forbid if overquota (checked on a probabilistic basis)
|
||||
- ``update_the_geom_webmercator`` before insert or update row to maintain the ``the_geom_webmercator`` updated with the contents in ``the_geom``
|
||||
|
||||
Described triggers example
|
||||
^^^^^^^^^^
|
||||
::
|
||||
|
||||
test_quota BEFORE INSERT OR UPDATE ON t FOR EACH STATEMENT EXECUTE PROCEDURE cdb_checkquota('0.1', '-1', 'public')
|
||||
test_quota_per_row BEFORE INSERT OR UPDATE ON t FOR EACH ROW EXECUTE PROCEDURE cdb_checkquota('0.001', '-1', 'public')
|
||||
track_updates AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON t FOR EACH STATEMENT EXECUTE PROCEDURE cdb_tablemetadata_trigger()
|
||||
update_the_geom_webmercator_trigger BEFORE INSERT OR UPDATE OF the_geom ON t FOR EACH ROW EXECUTE PROCEDURE _cdb_update_the_geom_webmercator()
|
||||
|
||||
|
||||
Further details
|
||||
----------
|
||||
|
||||
Some conversions will be attempted to perform upon cartodbfication when certain fields appear:
|
||||
|
||||
- ``cartodb_id``: If found type TEXT will be attempted to cast to integer. If not casteable, an eror will be raised.
|
||||
- ``the_geom``: If found type TEXT will be attempted to cast to geometry(Geometry,4326).
|
||||
@@ -1,59 +1,63 @@
|
||||
CartoDBfy Requirements
|
||||
======================
|
||||
|
||||
Introduction
|
||||
============
|
||||
------------
|
||||
|
||||
This document aims at describing what cartodbfy is and what its formal requirements are, with the following goals in mind:
|
||||
This document aims at describing what the CartoDBfication is and what its formal requirements are, with the following goals in mind:
|
||||
|
||||
- clarify what are the expectations of the "cartodbfycation process".
|
||||
- define an important part of what should be a stable, public API
|
||||
- allow for better testing, which should in turn...
|
||||
- Clarify what are the expectations of the "cartodbfycation process".
|
||||
- Define an important part of what should be a stable, public API
|
||||
- Allow for better testing, which should in turn...
|
||||
- ...ease modifications and increase quality of the code
|
||||
|
||||
|
||||
What is the CartoDBfycation
|
||||
---------------------------
|
||||
|
||||
What is the cartodbfycation
|
||||
===========================
|
||||
|
||||
The cartodbfycation is the process of converting an arbitrary postgres table into a valid CartoDB table, and register it in the system so that it can be used in the CartoDB editor and platform to generate maps and analysis.
|
||||
|
||||
The CartoDBfycation is the process of converting an arbitrary postgres table into a valid CartoDB table, and register it in the system so that it can be used in the CartoDB editor and platform to generate maps and analysis.
|
||||
|
||||
It is performed by running the function ``CDB_CartodbfyTable(reloid REGCLASS)`` over a target table.
|
||||
|
||||
Valid CartoDB tables
|
||||
====================
|
||||
--------------------
|
||||
|
||||
A valid CartoDB table shall meet the following conditions:
|
||||
|
||||
- Have a ``cartodb_id`` integer column as primary key with a sequence as default value
|
||||
- Have a ``cartodb_id`` column with integer, unique, non-zero and non-null values as primary key with a sequence as default value
|
||||
- Have a ``the_geom`` column of type ``Geometry`` with SRID 4326
|
||||
- Have a ``the_geom_webmercator`` column of type ``Geometry`` with SRID 3857
|
||||
- The columns ``the_geom`` and ``the_geom_webmercator`` shall be in sync
|
||||
- The columns ``the_geom`` and ``the_geom_webmercator`` shall be in sync (task of the ``update_the_geom_webmercator`` trigger)
|
||||
|
||||
Additionally, a CartoDB table can contain other columns.
|
||||
|
||||
See the `CartoDB User Table documentation`_
|
||||
|
||||
.. _CartoDB User Table documentation: https://github.com/CartoDB/cartodb-postgresql/blob/master/doc/CartoDB-user-table.md
|
||||
for further information.
|
||||
|
||||
High level requirements
|
||||
=======================
|
||||
-----------------------
|
||||
|
||||
Here is a list of high level requirments for the public function ``CDB_CartodbfyTable()``:
|
||||
|
||||
- A call to ``CDB_CartodbfyTable()`` shall modify/rewrite the table and produce a valid CartoDB table with the same name.
|
||||
- A call to ``CDB_CartodbfyTable()`` shall cause the registration of the table into the platform
|
||||
- It shall be idempotent, meaning that successive calls to ``CDB_CartodbfyTable()`` shall not produce any visible effect in the system.
|
||||
- A call to the function shall modify/rewrite the table and produce a valid CartoDB table with the same name.
|
||||
- A call to the function shall cause the registration of the table into the platform.
|
||||
- It shall be idempotent, meaning that successive calls to the function shall not produce any visible effect in the system.
|
||||
- If there's a column containing a geometry, it shall be used to generate ``the_geom`` and the ``the_geom_webmercator`` columns.
|
||||
- Exporting and re-importing the same table in CartoDB shall produce equivalent tables, with the same features associated to the same ``cartodb_id``'s.
|
||||
|
||||
Note that there should be only one feature per row in the source table. If there's more than one, then which one is used for ``the_geom`` and ``the_geom_webmercator`` fields is not determined.
|
||||
|
||||
Note that there should be only one geometry per row in the source table. If there's more than one, then which one is used for ``the_geom`` and ``the_geom_webmercator`` fields is not determined.
|
||||
|
||||
|
||||
Low-level requirements
|
||||
======================
|
||||
----------------------
|
||||
|
||||
- If the original table contains a valid (unique and not null) ``cartodb_id`` column, it shall be used
|
||||
- If the original table contains a ``the_geom`` column or a ``the_geom_webmercator`` column in the expected projection (EPSG 4326 and EPSG 3857, respectively) they shall be used.
|
||||
- If the original table contains a valid (integer, unique, non-zero and not null) ``cartodb_id`` column, it shall be used
|
||||
- If the original table contains a ``the_geom`` column or a ``the_geom_webmercator`` geometric column in the expected projection (EPSG 4326 and EPSG 3857, respectively) they shall be used.
|
||||
- A modification of a cartodbfy'ed table shall insert or update a row in ``CDB_TableMetadata``
|
||||
- A cartodbfy'ed table shall have a ``btree`` index on ``cartodb_id``
|
||||
- A cartodbfy'ed table shall have ``gist`` indices on ``the_geom`` and ``the_geom_webmercator``
|
||||
- Cartodbfy shall deal with text columns for imports, regarding CartoDB columns
|
||||
|
||||
- Cartodbfy shall deal with text columns for imports, regarding CartoDB columns (``cartodb_id``, ``the_geom``, ``the_geom_webmercator``)
|
||||
|
||||
|
||||
@@ -470,6 +470,7 @@ $$ LANGUAGE 'plpgsql';
|
||||
-- If the table has both a usable key and usable geometry
|
||||
-- we can no-op on the table copy and just ensure that the
|
||||
-- indexes and triggers are in place
|
||||
DROP FUNCTION IF EXISTS _CDB_Has_Usable_Primary_ID(reloid REGCLASS);
|
||||
CREATE OR REPLACE FUNCTION _CDB_Has_Usable_Primary_ID(reloid REGCLASS)
|
||||
RETURNS BOOLEAN
|
||||
AS $$
|
||||
@@ -489,114 +490,98 @@ BEGIN
|
||||
-- Do we already have a properly named column?
|
||||
SELECT a.attname, i.indisprimary, i.indisunique, a.attnotnull, a.atttypid
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_type t ON a.atttypid = t.oid
|
||||
LEFT JOIN pg_index i ON c.oid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE c.oid = reloid
|
||||
WHERE c.oid = reloid
|
||||
AND NOT a.attisdropped
|
||||
AND a.attname = const.pkey;
|
||||
|
||||
-- Found something named right...
|
||||
IF FOUND THEN
|
||||
|
||||
-- And it's an integer column...
|
||||
IF rec.atttypid IN (20,21,23) THEN
|
||||
|
||||
-- And it's a unique primary key! Done!
|
||||
IF (rec.indisprimary OR rec.indisunique) AND rec.attnotnull THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('found good ''%s''', const.pkey);
|
||||
RETURN true;
|
||||
|
||||
-- Check and see if the column values are unique and not null,
|
||||
-- if they are, we can use this column...
|
||||
-- And it's a unique primary key! Done!
|
||||
IF (rec.indisprimary OR rec.indisunique) AND rec.attnotnull THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('found good ''%s''', const.pkey);
|
||||
RETURN true;
|
||||
|
||||
-- Check and see if the column values are unique and not null,
|
||||
-- if they are, we can use this column...
|
||||
ELSE
|
||||
|
||||
-- Assume things are OK until proven otherwise...
|
||||
useable_key := true;
|
||||
BEGIN
|
||||
sql := Format('ALTER TABLE %s ADD CONSTRAINT %s_pk PRIMARY KEY (%s)', reloid::text, const.pkey, const.pkey);
|
||||
sql := sql || ', ' || Format('ADD CONSTRAINT %s_integer CHECK (%s::integer >=0);', const.pkey, const.pkey);
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', sql;
|
||||
EXECUTE sql;
|
||||
EXCEPTION
|
||||
-- Failed unique check...
|
||||
WHEN unique_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s is not unique', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed not null check...
|
||||
WHEN not_null_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s contains nulls', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed integer check...
|
||||
WHEN invalid_text_representation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('invalid input syntax for integer %s', const.pkey);
|
||||
useable_key := false;
|
||||
-- Other fatal error
|
||||
WHEN others THEN
|
||||
PERFORM _CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: %s', SQLERRM));
|
||||
END;
|
||||
|
||||
-- Clean up test constraint
|
||||
IF useable_key THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_pk', reloid::text, const.pkey));
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_integer', reloid::text, const.pkey));
|
||||
|
||||
-- Move non-valid column out of the way
|
||||
ELSE
|
||||
|
||||
-- Assume things are OK until proven otherwise...
|
||||
useable_key := true;
|
||||
|
||||
BEGIN
|
||||
sql := Format('ALTER TABLE %s ADD CONSTRAINT %s_pk PRIMARY KEY (%s)', reloid::text, const.pkey, const.pkey);
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', sql;
|
||||
EXECUTE sql;
|
||||
EXCEPTION
|
||||
-- Failed unique check...
|
||||
WHEN unique_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s is not unique', const.pkey);
|
||||
useable_key := false;
|
||||
-- Failed not null check...
|
||||
WHEN not_null_violation THEN
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', Format('column %s contains nulls', const.pkey);
|
||||
useable_key := false;
|
||||
-- Other fatal error
|
||||
WHEN others THEN
|
||||
PERFORM _CDB_Error(sql, '_CDB_Has_Usable_Primary_ID');
|
||||
END;
|
||||
|
||||
-- Clean up test constraint
|
||||
IF useable_key THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s DROP CONSTRAINT %s_pk', reloid::text, const.pkey));
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-valid ''%s''', const.pkey);
|
||||
|
||||
-- Move non-unique column out of the way
|
||||
ELSE
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-unique ''%s'', renaming it', const.pkey);
|
||||
|
||||
PERFORM _CDB_SQL(
|
||||
Format('ALTER TABLE %s RENAME COLUMN %s TO %I',
|
||||
reloid::text, rec.attname,
|
||||
cartodb._CDB_Unique_Column_Identifier(NULL, const.pkey, NULL, reloid)),
|
||||
'_CDB_Has_Usable_Primary_ID');
|
||||
|
||||
END IF;
|
||||
|
||||
return useable_key;
|
||||
PERFORM _CDB_Error(sql, Format('_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, %s', const.pkey));
|
||||
|
||||
END IF;
|
||||
|
||||
-- It's not an integer column, we have to rename it
|
||||
ELSE
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found non-integer ''%s'', renaming it', const.pkey);
|
||||
|
||||
PERFORM _CDB_SQL(
|
||||
Format('ALTER TABLE %s RENAME COLUMN %s TO %I',
|
||||
reloid::text, rec.attname, cartodb._CDB_Unique_Column_Identifier(NULL, const.pkey, NULL, reloid)),
|
||||
'_CDB_Has_Usable_Primary_ID');
|
||||
|
||||
RETURN useable_key;
|
||||
|
||||
END IF;
|
||||
|
||||
|
||||
-- There's no column there named pkey
|
||||
ELSE
|
||||
|
||||
-- Is there another suitable primary key already?
|
||||
-- Is there another integer suitable primary key already?
|
||||
SELECT a.attname
|
||||
INTO rec
|
||||
FROM pg_class c
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_attribute a ON a.attrelid = c.oid
|
||||
JOIN pg_type t ON a.atttypid = t.oid
|
||||
LEFT JOIN pg_index i ON c.oid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||
WHERE c.oid = reloid AND NOT a.attisdropped
|
||||
AND i.indisprimary AND i.indisunique AND a.attnotnull AND a.atttypid IN (20,21,23);
|
||||
|
||||
|
||||
-- Yes! Ok, rename it.
|
||||
IF FOUND THEN
|
||||
PERFORM _CDB_SQL(Format('ALTER TABLE %s RENAME COLUMN %s TO %s', reloid::text, rec.attname, const.pkey),'_CDB_Has_Usable_Primary_ID');
|
||||
RETURN true;
|
||||
ELSE
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %',
|
||||
Format('found no useful column for ''%s''', const.pkey);
|
||||
END IF;
|
||||
|
||||
|
||||
END IF;
|
||||
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Has_Usable_Primary_ID): %', 'function complete';
|
||||
|
||||
-- Didn't find re-usable key, so return FALSE
|
||||
RETURN false;
|
||||
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql';
|
||||
|
||||
@@ -825,10 +810,10 @@ DECLARE
|
||||
str TEXT;
|
||||
table_srid INTEGER;
|
||||
geom_srid INTEGER;
|
||||
|
||||
|
||||
has_usable_primary_key BOOLEAN;
|
||||
has_usable_pk_sequence BOOLEAN;
|
||||
|
||||
|
||||
BEGIN
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): %', 'entered function';
|
||||
@@ -839,7 +824,7 @@ BEGIN
|
||||
-- Save the raw schema/table names for later
|
||||
SELECT n.nspname, c.relname, c.relname
|
||||
INTO STRICT relschema, relname, destname
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.oid = reloid;
|
||||
|
||||
-- Default the destination to current schema if unspecified
|
||||
@@ -890,7 +875,7 @@ BEGIN
|
||||
|
||||
RAISE DEBUG 'CDB(_CDB_Rewrite_Table): has_usable_geoms %', gc.has_usable_geoms;
|
||||
|
||||
-- We can only avoid a rewrite if both the key and
|
||||
-- We can only avoid a rewrite if both the key and
|
||||
-- geometry are usable
|
||||
|
||||
-- No table re-write is required, BUT a rename is required to
|
||||
@@ -934,7 +919,7 @@ BEGIN
|
||||
|
||||
-- Add cartodb ID!
|
||||
IF has_usable_primary_key THEN
|
||||
sql := sql || const.pkey;
|
||||
sql := sql || const.pkey || '::integer ';
|
||||
ELSE
|
||||
sql := sql || 'nextval(''' || destseq || ''') AS ' || const.pkey;
|
||||
END IF;
|
||||
@@ -1096,7 +1081,7 @@ BEGIN
|
||||
|
||||
-- Run it!
|
||||
PERFORM _CDB_SQL(sql, '_CDB_Rewrite_Table');
|
||||
|
||||
|
||||
-- Set up the primary key sequence
|
||||
-- If we copied the primary key from the original data, we need
|
||||
-- to set the sequence to the maximum value of that key
|
||||
|
||||
@@ -486,6 +486,8 @@ BEGIN
|
||||
-- Using 'string_agg(' || qualified_column || ',''/'')'
|
||||
-- here causes
|
||||
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
WHEN 'boolean' THEN
|
||||
RETURN 'CASE count(*) WHEN 1 THEN BOOL_AND(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
ELSE
|
||||
RETURN 'CASE count(*) WHEN 1 THEN MIN(' || qualified_column || ') ELSE NULL END::' || column_type;
|
||||
END CASE;
|
||||
|
||||
@@ -66,9 +66,11 @@ CREATE OR REPLACE FUNCTION _CDB_TableMetadata_Updated()
|
||||
RETURNS trigger AS
|
||||
$$
|
||||
DECLARE
|
||||
tabname TEXT;
|
||||
tabname regclass;
|
||||
rec RECORD;
|
||||
found BOOL;
|
||||
schema_name TEXT;
|
||||
table_name TEXT;
|
||||
BEGIN
|
||||
|
||||
IF TG_OP = 'UPDATE' or TG_OP = 'INSERT' THEN
|
||||
@@ -103,9 +105,10 @@ BEGIN
|
||||
AND u.usesuper
|
||||
ORDER BY n.nspname
|
||||
LOOP
|
||||
SELECT n.nspname, c.relname FROM pg_class c, pg_namespace n WHERE c.oid=tabname AND c.relnamespace = n.oid INTO schema_name, table_name;
|
||||
EXECUTE 'SELECT ' || quote_ident(rec.nspname) || '.'
|
||||
|| quote_ident(rec.proname)
|
||||
|| '(' || quote_literal(tabname) || ')';
|
||||
|| '(' || quote_literal(quote_ident(schema_name) || '.' || quote_ident(table_name)) || ')';
|
||||
found := true;
|
||||
EXIT;
|
||||
END LOOP;
|
||||
@@ -116,11 +119,11 @@ END;
|
||||
$$
|
||||
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
|
||||
|
||||
DROP TRIGGER IF EXISTS table_modified ON CDB_TableMetadata;
|
||||
DROP TRIGGER IF EXISTS table_modified ON public.CDB_TableMetadata;
|
||||
-- NOTE: on DELETE we would be unable to convert the table
|
||||
-- oid (regclass) to its name
|
||||
CREATE TRIGGER table_modified AFTER INSERT OR UPDATE
|
||||
ON CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
ON public.CDB_TableMetadata FOR EACH ROW EXECUTE PROCEDURE
|
||||
_CDB_TableMetadata_Updated();
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SET client_min_messages TO error;
|
||||
\set VERBOSITY default
|
||||
\set VERBOSITY terse
|
||||
|
||||
CREATE OR REPLACE FUNCTION CDB_CartodbfyTableCheck(tabname regclass, label text)
|
||||
RETURNS text AS
|
||||
@@ -124,13 +124,21 @@ END;
|
||||
$$
|
||||
LANGUAGE 'plpgsql';
|
||||
|
||||
-- table with single non-geometrical column
|
||||
-- check cartodbfytable idempotence
|
||||
CREATE TABLE t AS SELECT 1::int as a;
|
||||
SELECT CDB_CartodbfyTable('public', 't'); -- should fail
|
||||
SELECT CDB_SetUserQuotaInBytes(0); -- Set user quota to infinite
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'single non-geometrical column');
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with single non-geometrical column
|
||||
CREATE TABLE t AS SELECT ST_SetSRID(ST_MakePoint(-1,-1),4326) as the_geom, 1::int as cartodb_id, 'this is a sentence' as description;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'check function idempotence');
|
||||
SELECT * FROM t;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'check function idempotence');
|
||||
SELECT * FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with existing srid-unconstrained (but type-constrained) the_geom
|
||||
CREATE TABLE t AS SELECT ST_SetSRID(ST_MakePoint(0,0),4326)::geometry(point) as the_geom;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'srid-unconstrained the_geom');
|
||||
@@ -164,19 +172,22 @@ SELECT CDB_CartodbfyTableCheck('t', 'trigger-protected the_geom');
|
||||
SELECT 'extent',ST_Extent(ST_SnapToGrid(the_geom,0.2)) FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- INFO: disabled because cartodbfy does not longer consider text columns for primary ID
|
||||
-- -- table with existing cartodb_id field of type text
|
||||
-- CREATE TABLE t AS SELECT 10::text as cartodb_id;
|
||||
-- SELECT CDB_CartodbfyTableCheck('t', 'text cartodb_id');
|
||||
-- select cartodb_id/2 FROM t;
|
||||
-- DROP TABLE t;
|
||||
-- table with existing cartodb_id field of type text
|
||||
CREATE TABLE t AS SELECT 10::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'text cartodb_id');
|
||||
select cartodb_id/2 FROM t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- INFO: disabled because cartodbfy does not longer consider text columns for primary ID
|
||||
-- -- table with existing cartodb_id field of type text not casting
|
||||
-- CREATE TABLE t AS SELECT 'nan' as cartodb_id;
|
||||
-- SELECT CDB_CartodbfyTableCheck('t', 'uncasting text cartodb_id');
|
||||
-- select cartodb_id,_cartodb_id0 FROM t;
|
||||
-- DROP TABLE t;
|
||||
-- table with existing cartodb_id field of type text not casting
|
||||
CREATE TABLE t AS SELECT 'nan'::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'uncasting text cartodb_id');
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with empty cartodb_id field of type text
|
||||
CREATE TABLE t AS SELECT null::text as cartodb_id;
|
||||
SELECT CDB_CartodbfyTableCheck('t', 'empty text cartodb_id');
|
||||
SELECT cartodb_id from t;
|
||||
DROP TABLE t;
|
||||
|
||||
-- table with existing cartodb_id field of type int4 not sequenced
|
||||
CREATE TABLE t AS SELECT 1::int4 as cartodb_id;
|
||||
@@ -291,7 +302,7 @@ INSERT INTO test VALUES
|
||||
(NULL),
|
||||
(3);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with null cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
-- Table with non unique cartodb_id
|
||||
@@ -303,7 +314,7 @@ INSERT INTO test VALUES
|
||||
(2),
|
||||
(2);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with non unique cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
-- Table with non unique and null cartodb_id
|
||||
@@ -316,7 +327,7 @@ INSERT INTO test VALUES
|
||||
(NULL),
|
||||
(2);
|
||||
SELECT CDB_CartodbfyTableCheck('test', 'Table with non unique and null cartodb_id #148');
|
||||
SELECT cartodb_id, cartodb_id_0 from test;
|
||||
SELECT cartodb_id from test;
|
||||
DROP TABLE test;
|
||||
|
||||
CREATE TABLE test (
|
||||
|
||||
@@ -2,12 +2,16 @@ SET
|
||||
CREATE FUNCTION
|
||||
SELECT 1
|
||||
ERROR: Please set user quota before cartodbfying tables.
|
||||
CONTEXT: SQL statement "SELECT cartodb._CDB_check_prerequisites(destschema, reloid)"
|
||||
PL/pgSQL function cdb_cartodbfytable(text,regclass) line 21 at PERFORM
|
||||
0
|
||||
single non-geometrical column cartodbfied fine
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
check function idempotence cartodbfied fine
|
||||
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
|
||||
check function idempotence cartodbfied fine
|
||||
1|0101000020E6100000000000000000F0BF000000000000F0BF|0101000020110F0000DB0B4ADA772DFBC077432E49D22DFBC0|this is a sentence
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
srid-unconstrained the_geom cartodbfied fine
|
||||
DROP TABLE
|
||||
SELECT 2
|
||||
@@ -26,6 +30,17 @@ SELECT 1
|
||||
CREATE TRIGGER
|
||||
trigger-protected the_geom cartodbfied fine
|
||||
extent|BOX(1 1,2 2)
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
text cartodb_id cartodbfied fine
|
||||
5
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE t ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE t ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
|
||||
DROP TABLE
|
||||
SELECT 1
|
||||
unsequenced cartodb_id cartodbfied fine
|
||||
@@ -77,30 +92,30 @@ Table with both the_geom and wkb_geometry #141 cartodbfied fine
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 1
|
||||
Many colliding columns #141 cartodbfied fine
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: multiple primary keys for table "many_colliding_columns" are not allowed): ALTER TABLE many_colliding_columns ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 4
|
||||
Table with null cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|
|
||||
4|3
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 3
|
||||
Table with non unique cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|2
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
2
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
INSERT 0 4
|
||||
Table with non unique and null cartodb_id #148 cartodbfied fine
|
||||
1|1
|
||||
2|2
|
||||
3|
|
||||
4|2
|
||||
ERROR: CDB(_CDB_Has_Usable_Primary_ID: Error: invalid cartodb_id, cartodb_id): ALTER TABLE test ADD CONSTRAINT cartodb_id_pk PRIMARY KEY (cartodb_id), ADD CONSTRAINT cartodb_id_integer CHECK (cartodb_id::integer >=0);
|
||||
1
|
||||
2
|
||||
|
||||
2
|
||||
DROP TABLE
|
||||
CREATE TABLE
|
||||
CREATE INDEX
|
||||
|
||||
@@ -20,14 +20,19 @@ SELECT count(*) FROM _vovw_5_base_t;
|
||||
|
||||
SELECT CDB_CreateOverviews('polyg_t'::regclass);
|
||||
|
||||
SELECT CDB_CreateOverviews('column_types_t'::regclass);
|
||||
|
||||
SELECT CDB_Overviews('base_t'::regclass);
|
||||
SELECT CDB_Overviews(ARRAY['base_t'::regclass, 'base_bare_t'::regclass]);
|
||||
SELECT CDB_Overviews('polyg_t'::regclass);
|
||||
SELECT CDB_Overviews('column_types_t'::regclass);
|
||||
|
||||
SELECT CDB_DropOverviews('column_types_t'::regclass);
|
||||
SELECT CDB_DropOverviews('base_bare_t'::regclass);
|
||||
SELECT CDB_DropOverviews('base_t'::regclass);
|
||||
SELECT count(*) FROM _vovw_5_base_t;
|
||||
|
||||
DROP TABLE column_types_t;
|
||||
DROP TABLE base_bare_t;
|
||||
DROP TABLE base_t;
|
||||
DROP TABLE polyg_t;
|
||||
|
||||
@@ -5,6 +5,7 @@ CREATE TABLE
|
||||
INSERT 0 1114
|
||||
CREATE TABLE
|
||||
INSERT 0 5
|
||||
SELECT 1114
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +17,7 @@ AVG(tab.number)::double precision AS number,AVG(tab.int_number)::integer AS int_
|
||||
{_vovw_5_base_t,_vovw_4_base_t,_vovw_3_base_t,_vovw_2_base_t,_vovw_1_base_t,_vovw_0_base_t}
|
||||
125
|
||||
|
||||
{_vovw_5_column_types_t,_vovw_4_column_types_t,_vovw_3_column_types_t,_vovw_2_column_types_t,_vovw_1_column_types_t,_vovw_0_column_types_t}
|
||||
(base_t,0,_vovw_0_base_t)
|
||||
(base_t,1,_vovw_1_base_t)
|
||||
(base_t,2,_vovw_2_base_t)
|
||||
@@ -34,6 +36,13 @@ AVG(tab.number)::double precision AS number,AVG(tab.int_number)::integer AS int_
|
||||
(base_t,3,_vovw_3_base_t)
|
||||
(base_t,4,_vovw_4_base_t)
|
||||
(base_t,5,_vovw_5_base_t)
|
||||
(column_types_t,0,_vovw_0_column_types_t)
|
||||
(column_types_t,1,_vovw_1_column_types_t)
|
||||
(column_types_t,2,_vovw_2_column_types_t)
|
||||
(column_types_t,3,_vovw_3_column_types_t)
|
||||
(column_types_t,4,_vovw_4_column_types_t)
|
||||
(column_types_t,5,_vovw_5_column_types_t)
|
||||
|
||||
|
||||
|
||||
ERROR: relation "_vovw_5_base_t" does not exist
|
||||
@@ -42,3 +51,4 @@ LINE 1: SELECT count(*) FROM _vovw_5_base_t;
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
DROP TABLE
|
||||
|
||||
@@ -350,6 +350,41 @@ function test_cdb_tablemetadatatouch_fails_from_user_without_permission() {
|
||||
sql postgres "REVOKE ALL ON CDB_TableMetadata FROM cdb_testmember_1;"
|
||||
}
|
||||
|
||||
function test_cdb_tablemetadatatouch_fully_qualifies_names() {
|
||||
sql postgres "CREATE TABLE touch_invalidations (table_name text);"
|
||||
sql postgres "create or replace function cartodb.cdb_invalidate_varnish(table_name text) returns void as \$\$ begin insert into touch_invalidations select table_name; end; \$\$ language 'plpgsql';"
|
||||
|
||||
#default schema
|
||||
sql "CREATE TABLE touch_example (a int);"
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "public.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
sql postgres "DROP TABLE touch_example"
|
||||
|
||||
#setup different schema
|
||||
sql postgres "CREATE SCHEMA test_schema;"
|
||||
sql postgres "CREATE TABLE test_schema.touch_example (a int);"
|
||||
|
||||
#different schema outside search_path
|
||||
sql postgres "SELECT CDB_TableMetadataTouch('test_schema.touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "test_schema.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
|
||||
#different schema in default search_path
|
||||
sql postgres "SET search_path=test_schema,public,cartodb; SELECT CDB_TableMetadataTouch('test_schema.touch_example');"
|
||||
sql postgres "SELECT table_name FROM touch_invalidations" should "test_schema.touch_example"
|
||||
sql postgres "TRUNCATE TABLE touch_invalidations"
|
||||
|
||||
#teardown different schema
|
||||
sql postgres 'DROP TABLE test_schema.touch_example;'
|
||||
sql postgres 'DROP SCHEMA test_schema;'
|
||||
|
||||
|
||||
|
||||
sql postgres 'DROP FUNCTION cartodb.cdb_invalidate_varnish(table_name text);'
|
||||
sql postgres 'DROP TABLE touch_invalidations'
|
||||
}
|
||||
|
||||
function test_cdb_column_names() {
|
||||
sql cdb_testmember_1 'CREATE TABLE cdb_testmember_1.table_cnames(c int, a int, r int, t int, o int);'
|
||||
sql cdb_testmember_2 'CREATE TABLE cdb_testmember_2.table_cnames(d int, b int);'
|
||||
|
||||
@@ -2240,3 +2240,10 @@ INSERT INTO polyg_t VALUES
|
||||
(3, 'C', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857)),
|
||||
(4, 'D', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857)),
|
||||
(5, 'E', 'SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, ST_Transform('SRID=4326;POLYGON((9 40,8 39,8.5 40,9 41,9 40))'::geometry, 3857));
|
||||
|
||||
CREATE TABLE column_types_t
|
||||
AS SELECT cartodb_id,
|
||||
the_geom,
|
||||
the_geom_webmercator,
|
||||
(CASE cartodb_id % 2 WHEN 0 THEN 'f' ELSE 't' END)::boolean AS is_odd
|
||||
FROM base_bare_t;
|
||||
|
||||
Reference in New Issue
Block a user