diff --git a/README.md b/README.md index 8fa7e55..561782b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ end ## Other options +### indexes_after_tables + You can optionally have indexes following the respective tables setting `indexes_after_tables`: ```ruby @@ -61,6 +63,8 @@ CREATE INDEX index_users_on_tentant_id ON public.users USING btree (tenant_id); CREATE UNIQUE INDEX index_users_on_email ON public.users USING btree (email); ``` +### order_column_definitions + To enable sorting the table column definitions alphabetically, discarding the actual order provided by `pg_dump`, set `order_column_definitions`: ```ruby @@ -69,6 +73,8 @@ Rails.application.configure do end ``` +### order_schema_migrations_values + You can have the schema_migrations values reorganized to prevent merge conflicts by setting `order_schema_migrations_values`: ```ruby @@ -87,6 +93,8 @@ INSERT INTO "schema_migrations" (version) VALUES ; ``` +### keep_extensions + By default the gem will remove [some extensions](https://github.com/ghiculescu/activerecord-clean-db-structure/blob/c9551391476a5e7a08ff314501af89baddcf669a/lib/activerecord-clean-db-structure/clean_dump.rb#L24) that typically aren't needed in structure dumps. You can choose to keep all, or just some, of those extensions: ```ruby @@ -94,7 +102,17 @@ Rails.application.configure do config.activerecord_clean_db_structure.keep_extensions = :all # This does the same thing as :all. You can choose which optional extensions to keep. - config.activerecord_clean_db_structure.keep_extensions = ["pg_stat_statements", "pg_buffercache"] + config.activerecord_clean_db_structure.keep_extensions = ['pg_stat_statements', 'pg_buffercache'] +end +``` + +### ignore_schmeas + +You can ignore specific schemas, for example when dumping from a database copy that is integrated with pganalyze and has helper functions: + +```ruby +Rails.application.configure do + config.activerecord_clean_db_structure.ignore_schemas = ['pganalyze'] end ``` diff --git a/lib/activerecord-clean-db-structure/clean_dump.rb b/lib/activerecord-clean-db-structure/clean_dump.rb index 0f37c67..f95d71a 100644 --- a/lib/activerecord-clean-db-structure/clean_dump.rb +++ b/lib/activerecord-clean-db-structure/clean_dump.rb @@ -10,8 +10,10 @@ def initialize(dump, options = {}) def run clean_partition_tables # Must be first because it makes assumptions about string format clean + clean_ignored_schemas clean_inherited_tables clean_options + clean_schema_comments end def clean @@ -44,7 +46,7 @@ def clean dump.gsub!(/^COMMENT ON EXTENSION .*/, '') # Remove useless, version-specific parts of comments - dump.gsub!(/^-- (.*); Schema: ([\w\.]+|-); Owner: -.*/, '-- \1') + dump.gsub!(/^-- (.*); Owner: -.*/, '-- \1') # Remove useless comment lines dump.gsub!(/^--$/, '') @@ -61,14 +63,14 @@ def clean dump.gsub!(/^ALTER SEQUENCE [\w\.]+_id_seq OWNED BY .*;$/, '') dump.gsub!(/^ALTER TABLE ONLY [\w\.]+ ALTER COLUMN id SET DEFAULT nextval\('[\w\.]+_id_seq'::regclass\);$/, '') dump.gsub!(/^ALTER TABLE ONLY [\w\.]+\s+ADD CONSTRAINT [\w\.]+_pkey PRIMARY KEY \(id\);$/, '') - dump.gsub!(/^-- Name: (\w+\s+)?id; Type: DEFAULT$/, '') + dump.gsub!(/^-- Name: (\w+\s+)?id; Type: DEFAULT; Schema: \w+$/, '') dump.gsub!(/^-- .*_id_seq; Type: SEQUENCE.*/, '') - dump.gsub!(/^-- Name: (\w+\s+)?\w+_pkey; Type: CONSTRAINT$/, '') + dump.gsub!(/^-- Name: (\w+\s+)?\w+_pkey; Type: CONSTRAINT; Schema: \w+$/, '') end end def clean_inherited_tables - inherited_tables_regexp = /-- Name: ([\w\.]+); Type: TABLE\n\n[^;]+?INHERITS \([\w\.]+\);/m + inherited_tables_regexp = /-- Name: ([\w\.]+); Type: TABLE; Schema: \w+\n\n[^;]+?INHERITS \([\w\.]+\);/m inherited_tables = dump.scan(inherited_tables_regexp).map(&:first) dump.gsub!(inherited_tables_regexp, '') inherited_tables.each do |inherited_table| @@ -76,12 +78,21 @@ def clean_inherited_tables index_regexp = /CREATE INDEX ([\w_]+) ON ([\w_]+\.)?#{inherited_table}[^;]+;/m dump.scan(index_regexp).map(&:first).each do |inherited_table_index| - dump.gsub!("-- Name: #{inherited_table_index}; Type: INDEX", '') + dump.gsub!(/-- Name: #{inherited_table_index}; Type: INDEX; Schema: \w+/, '') end dump.gsub!(index_regexp, '') end end + def clean_ignored_schemas + return if options[:ignore_schemas].nil? + options[:ignore_schemas].each do |schema| + dump.gsub!(/-- Name: ([^;]+); Type: \w+; Schema: #{schema}\n\n(?:(?!(-- Name:|SET default_tablespace)).)*/m, '') + dump.gsub!(/-- Name: #{schema}; Type: SCHEMA; Schema: -\n\n(?:(?!(-- Name:|SET default_tablespace)).)*/m, '') + dump.gsub!(/-- Name: \w+; Type: EXTENSION; Schema: -\n\n\nCREATE EXTENSION IF NOT EXISTS \w+ WITH SCHEMA #{schema};(?:(?!(-- Name:|SET default_tablespace)).)*/m, '') + end + end + def clean_partition_tables partitioned_tables = [] @@ -99,7 +110,7 @@ def clean_partition_tables names = [] partitioned_tables.each { |table| names << table.split('.', 2)[1] } if names.any? - dump.scan(/CREATE (UNIQUE )?INDEX ([\w_]+) ON ([\w_]+\.)?(#{names.join('|')})[^;]+;/m).each { |m| names << m[1] } + dump.scan(/CREATE (UNIQUE )?INDEX (\w+) ON (\w+\.)?(#{names.join('|')})[^;]+;/m).each { |m| names << m[1] } end statements.reject! { |stmt| names.any? { |name| stmt.include?(name) } } @dump = statements.join("\n\n") @@ -126,7 +137,7 @@ def clean_options .transform_values(&:join) dump.gsub!(/^CREATE( UNIQUE)? INDEX \w+ ON .+\n+/, '') - dump.gsub!(/^-- Name: \w+; Type: INDEX\n+/, '') + dump.gsub!(/^-- Name: \w+; Type: INDEX; Schema: \w+\n+/, '') indexes.each do |table, indexes_for_table| dump.gsub!(/^(CREATE TABLE #{table}\b(:?[^;\n]*\n)+\);*\n(?:.*);*)/) { $1 + "\n\n" + indexes_for_table } end @@ -140,6 +151,11 @@ def clean_options end end + def clean_schema_comments + # Remove schema in comments for backwards compatibility + dump.gsub!(/^-- (.*); Schema: [\w-]+/, '-- \1') + end + def order_column_definitions(source) result = [] diff --git a/test/clean_dump_test.rb b/test/clean_dump_test.rb index 08edf5a..9d72c2b 100644 --- a/test/clean_dump_test.rb +++ b/test/clean_dump_test.rb @@ -29,6 +29,11 @@ def test_partitions assert_cleans_dump "data/partitions.sql", "expectations/partitions.sql" end + def test_ignored_schemas + assert_cleans_dump "data/ignored_schemas.sql", "expectations/ignored_schemas_pganalyze.sql", ignore_schemas: ['pganalyze'] + assert_cleans_dump "data/ignored_schemas.sql", "expectations/ignored_schemas_myschema.sql", ignore_schemas: ['myschema'] + end + private def assert_cleans_dump(input, output, props = {}) diff --git a/test/data/ignored_schemas.sql b/test/data/ignored_schemas.sql new file mode 100644 index 0000000..bc61ce5 --- /dev/null +++ b/test/data/ignored_schemas.sql @@ -0,0 +1,279 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: myschema; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA myschema; + + +-- +-- Name: pganalyze; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pganalyze; + + +-- +-- Name: btree_gin; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gin WITH SCHEMA myschema; + + +-- +-- Name: EXTENSION btree_gin; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gin IS 'support for indexing common datatypes in GIN'; + + +-- +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + + +-- +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- Name: dblink; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS dblink WITH SCHEMA public; + + +-- +-- Name: EXTENSION dblink; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION dblink IS 'connect to other PostgreSQL databases from within a database'; + + +-- +-- Name: pg_buffercache; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_buffercache WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_buffercache; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_buffercache IS 'examine the shared buffer cache'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; + + +-- +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: myschema; Type: TYPE; Schema: myschema; Owner: - +-- + +CREATE TYPE myschema.myenum AS ENUM ( + 'a', + 'b', + 'c' +); + + +-- +-- Name: explain_analyze(text, text[], text[], text[]); Type: FUNCTION; Schema: pganalyze; Owner: - +-- + +CREATE FUNCTION pganalyze.explain_analyze(query text, params text[], param_types text[], analyze_flags text[]) RETURNS text + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + prepared_query text; + params_str text; + param_types_str text; + explain_prefix text; + explain_flag text; + result text; +BEGIN + SET TRANSACTION READ ONLY; + + PERFORM 1 FROM pg_roles WHERE (rolname = current_user AND rolsuper) OR (pg_has_role(oid, 'MEMBER') AND rolname IN ('rds_superuser', 'azure_pg_admin', 'cloudsqlsuperuser')); + IF FOUND THEN + RAISE EXCEPTION 'cannot run: pganalyze.explain_analyze helper is owned by superuser - recreate function with lesser privileged user'; + END IF; + + SELECT pg_catalog.regexp_replace(query, ';+\s*\Z', '') INTO prepared_query; + IF prepared_query LIKE '%;%' THEN + RAISE EXCEPTION 'cannot run pganalyze.explain_analyze helper with a multi-statement query'; + END IF; + + explain_prefix := 'EXPLAIN (VERBOSE, FORMAT JSON'; + FOR explain_flag IN SELECT * FROM unnest(analyze_flags) + LOOP + IF explain_flag NOT SIMILAR TO '[A-z_ ]+' THEN + RAISE EXCEPTION 'cannot run pganalyze.explain_analyze helper with invalid flag'; + END IF; + explain_prefix := explain_prefix || ', ' || explain_flag; + END LOOP; + explain_prefix := explain_prefix || ') '; + + SELECT COALESCE('(' || pg_catalog.string_agg(pg_catalog.quote_literal(p), ',') || ')', '') FROM pg_catalog.unnest(params) _(p) INTO params_str; + SELECT COALESCE('(' || pg_catalog.string_agg(pg_catalog.quote_ident(p), ',') || ')', '') FROM pg_catalog.unnest(param_types) _(p) INTO param_types_str; + + EXECUTE 'PREPARE pganalyze_explain_analyze ' || param_types_str || ' AS ' || prepared_query; + BEGIN + EXECUTE explain_prefix || 'EXECUTE pganalyze_explain_analyze' || params_str INTO STRICT result; + EXCEPTION WHEN QUERY_CANCELED OR OTHERS THEN + DEALLOCATE pganalyze_explain_analyze; + RAISE; + END; + DEALLOCATE pganalyze_explain_analyze; + + RETURN result; +END +$$; + + +-- +-- Name: get_stat_replication(); Type: FUNCTION; Schema: pganalyze; Owner: - +-- + +CREATE FUNCTION pganalyze.get_stat_replication() RETURNS SETOF pg_stat_replication + LANGUAGE sql SECURITY DEFINER + AS $$ + /* pganalyze-collector */ SELECT * FROM pg_catalog.pg_stat_replication; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: table2; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.table2 ( + id bigint NOT NULL, +); + + +-- +-- Name: test_func2(public.table2[]); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.test_func2(ids public.table2[] DEFAULT NULL::public.table2[]) RETURNS TABLE(id bigint) + LANGUAGE sql ROWS 10000 PARALLEL SAFE + AS $$ + SELECT * FROM +$$; + + +-- +-- Name: ar_internal_metadata; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ar_internal_metadata ( + key character varying NOT NULL, + value character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: myschema; Type: TABLE; Schema: myschema; Owner: - +-- + +CREATE TABLE myschema.mytable ( + id uuid DEFAULT public.gen_random_uuid() NOT NULL, + organization_id uuid NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: index_mytable_on_organization_id; Type: INDEX; Schema: myschema; Owner: - +-- + +CREATE INDEX index_mytable_on_organization_id ON myschema.mytable USING btree (organization_id); + + +-- +-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version); + + +-- +-- PostgreSQL database dump complete +-- + +SET search_path TO "$user", public; + +INSERT INTO "schema_migrations" (version) VALUES +('20250101011234'), +('20250101015678'); + diff --git a/test/expectations/ignored_schemas_myschema.sql b/test/expectations/ignored_schemas_myschema.sql new file mode 100644 index 0000000..df5450c --- /dev/null +++ b/test/expectations/ignored_schemas_myschema.sql @@ -0,0 +1,139 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- Name: pganalyze; Type: SCHEMA + +CREATE SCHEMA pganalyze; + +-- Name: EXTENSION btree_gin; Type: COMMENT + +-- Name: btree_gist; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + +-- Name: EXTENSION btree_gist; Type: COMMENT + +-- Name: dblink; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS dblink WITH SCHEMA public; + +-- Name: EXTENSION dblink; Type: COMMENT + +-- Name: pgcrypto; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; + +-- Name: EXTENSION pgcrypto; Type: COMMENT + +-- Name: uuid-ossp; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT + +-- Name: explain_analyze(text, text[], text[], text[]); Type: FUNCTION + +CREATE FUNCTION pganalyze.explain_analyze(query text, params text[], param_types text[], analyze_flags text[]) RETURNS text + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + prepared_query text; + params_str text; + param_types_str text; + explain_prefix text; + explain_flag text; + result text; +BEGIN + SET TRANSACTION READ ONLY; + + PERFORM 1 FROM pg_roles WHERE (rolname = current_user AND rolsuper) OR (pg_has_role(oid, 'MEMBER') AND rolname IN ('rds_superuser', 'azure_pg_admin', 'cloudsqlsuperuser')); + IF FOUND THEN + RAISE EXCEPTION 'cannot run: pganalyze.explain_analyze helper is owned by superuser - recreate function with lesser privileged user'; + END IF; + + SELECT pg_catalog.regexp_replace(query, ';+\s*\Z', '') INTO prepared_query; + IF prepared_query LIKE '%;%' THEN + RAISE EXCEPTION 'cannot run pganalyze.explain_analyze helper with a multi-statement query'; + END IF; + + explain_prefix := 'EXPLAIN (VERBOSE, FORMAT JSON'; + FOR explain_flag IN SELECT * FROM unnest(analyze_flags) + LOOP + IF explain_flag NOT SIMILAR TO '[A-z_ ]+' THEN + RAISE EXCEPTION 'cannot run pganalyze.explain_analyze helper with invalid flag'; + END IF; + explain_prefix := explain_prefix || ', ' || explain_flag; + END LOOP; + explain_prefix := explain_prefix || ') '; + + SELECT COALESCE('(' || pg_catalog.string_agg(pg_catalog.quote_literal(p), ',') || ')', '') FROM pg_catalog.unnest(params) _(p) INTO params_str; + SELECT COALESCE('(' || pg_catalog.string_agg(pg_catalog.quote_ident(p), ',') || ')', '') FROM pg_catalog.unnest(param_types) _(p) INTO param_types_str; + + EXECUTE 'PREPARE pganalyze_explain_analyze ' || param_types_str || ' AS ' || prepared_query; + BEGIN + EXECUTE explain_prefix || 'EXECUTE pganalyze_explain_analyze' || params_str INTO STRICT result; + EXCEPTION WHEN QUERY_CANCELED OR OTHERS THEN + DEALLOCATE pganalyze_explain_analyze; + RAISE; + END; + DEALLOCATE pganalyze_explain_analyze; + + RETURN result; +END +$$; + +-- Name: get_stat_replication(); Type: FUNCTION + +CREATE FUNCTION pganalyze.get_stat_replication() RETURNS SETOF pg_stat_replication + LANGUAGE sql SECURITY DEFINER + AS $$ + /* pganalyze-collector */ SELECT * FROM pg_catalog.pg_stat_replication; +$$; + +SET default_tablespace = ''; + +-- Name: table2; Type: TABLE + +CREATE TABLE public.table2 ( + id BIGSERIAL PRIMARY KEY, +); + +-- Name: test_func2(public.table2[]); Type: FUNCTION + +CREATE FUNCTION public.test_func2(ids public.table2[] DEFAULT NULL::public.table2[]) RETURNS TABLE(id bigint) + LANGUAGE sql ROWS 10000 PARALLEL SAFE + AS $$ + SELECT * FROM +$$; + +-- Name: ar_internal_metadata; Type: TABLE + +CREATE TABLE public.ar_internal_metadata ( + key character varying NOT NULL, + value character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + +-- Name: schema_migrations; Type: TABLE + +CREATE TABLE public.schema_migrations ( + version character varying(255) NOT NULL +); + +-- Name: unique_schema_migrations; Type: INDEX + +CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version); + +-- PostgreSQL database dump complete + +SET search_path TO "$user", public; + +INSERT INTO "schema_migrations" (version) VALUES +('20250101011234'), +('20250101015678'); diff --git a/test/expectations/ignored_schemas_pganalyze.sql b/test/expectations/ignored_schemas_pganalyze.sql new file mode 100644 index 0000000..ca2ffd1 --- /dev/null +++ b/test/expectations/ignored_schemas_pganalyze.sql @@ -0,0 +1,103 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- Name: myschema; Type: SCHEMA + +CREATE SCHEMA myschema; + +-- Name: btree_gin; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS btree_gin WITH SCHEMA myschema; + +-- Name: EXTENSION btree_gin; Type: COMMENT + +-- Name: btree_gist; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + +-- Name: EXTENSION btree_gist; Type: COMMENT + +-- Name: dblink; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS dblink WITH SCHEMA public; + +-- Name: EXTENSION dblink; Type: COMMENT + +-- Name: pgcrypto; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; + +-- Name: EXTENSION pgcrypto; Type: COMMENT + +-- Name: uuid-ossp; Type: EXTENSION + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT + +-- Name: myschema; Type: TYPE + +CREATE TYPE myschema.myenum AS ENUM ( + 'a', + 'b', + 'c' +); + +SET default_tablespace = ''; + +-- Name: table2; Type: TABLE + +CREATE TABLE public.table2 ( + id BIGSERIAL PRIMARY KEY, +); + +-- Name: test_func2(public.table2[]); Type: FUNCTION + +CREATE FUNCTION public.test_func2(ids public.table2[] DEFAULT NULL::public.table2[]) RETURNS TABLE(id bigint) + LANGUAGE sql ROWS 10000 PARALLEL SAFE + AS $$ + SELECT * FROM +$$; + +-- Name: ar_internal_metadata; Type: TABLE + +CREATE TABLE public.ar_internal_metadata ( + key character varying NOT NULL, + value character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + +-- Name: myschema; Type: TABLE + +CREATE TABLE myschema.mytable ( + id uuid DEFAULT public.gen_random_uuid() PRIMARY KEY, + organization_id uuid NOT NULL +); + +-- Name: schema_migrations; Type: TABLE + +CREATE TABLE public.schema_migrations ( + version character varying(255) NOT NULL +); + +-- Name: index_mytable_on_organization_id; Type: INDEX + +CREATE INDEX index_mytable_on_organization_id ON myschema.mytable USING btree (organization_id); + +-- Name: unique_schema_migrations; Type: INDEX + +CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version); + +-- PostgreSQL database dump complete + +SET search_path TO "$user", public; + +INSERT INTO "schema_migrations" (version) VALUES +('20250101011234'), +('20250101015678');