-- -- 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;