To use state-based workflow, you must start by exporting your database schema. Bytebase provides a built-in export feature that generates SDL files in the multi-file format, ready to commit to your Git repository.
Bytebase uses a multi-file format where each database object is stored in a separate file, organized by schema and object type. This structure provides clear organization and makes code reviews easier.
A table file includes the table definition and all related objects:
-- schemas/public/tables/users.sqlCREATE TABLE public.users ( id INTEGER NOT NULL DEFAULT nextval('public.users_id_seq'::regclass), username TEXT NOT NULL, email TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT users_pkey PRIMARY KEY (id), CONSTRAINT users_username_key UNIQUE (username), CONSTRAINT users_email_key UNIQUE (email));COMMENT ON TABLE public.users IS 'Application users';COMMENT ON COLUMN public.users.username IS 'Unique username for login';CREATE INDEX idx_users_email ON public.users(email);CREATE INDEX idx_users_created_at ON public.users(created_at);COMMENT ON INDEX idx_users_email IS 'Index for email lookups';-- Owned sequenceCREATE SEQUENCE public.users_id_seq;ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id;
Materialized view files include the view definition and its indexes:
-- schemas/public/materialized_views/sales_summary.sqlCREATE MATERIALIZED VIEW public.sales_summary ASSELECT DATE_TRUNC('day', created_at) AS sale_date, COUNT(*) AS total_orders, SUM(amount) AS total_amountFROM public.ordersGROUP BY sale_date;COMMENT ON MATERIALIZED VIEW public.sales_summary IS 'Daily sales aggregation';CREATE INDEX idx_sales_summary_date ON public.sales_summary(sale_date);COMMENT ON INDEX idx_sales_summary_date IS 'Index on sale date for fast lookups';
PRIMARY KEY, UNIQUE, FOREIGN KEY, and CHECK constraints must be defined at table level with explicit names:
✅ Correct - Table-level with namesCREATE TABLE public.users ( id INTEGER NOT NULL, -- NOT NULL allowed at column level email TEXT NOT NULL, -- NOT NULL allowed at column level created_at TIMESTAMP DEFAULT NOW(), -- DEFAULT allowed at column level CONSTRAINT users_pkey PRIMARY KEY (id), CONSTRAINT users_email_key UNIQUE (email), CONSTRAINT users_email_check CHECK (email LIKE '%@%'));
❌ Incorrect - Column-level constraintsCREATE TABLE public.users ( id INTEGER PRIMARY KEY, -- Must be at table level email TEXT UNIQUE, -- Must be at table level age INTEGER CHECK (age >= 0) -- Must be at table level);
CREATE TABLE public.users ( id INTEGER NOT NULL DEFAULT nextval('public.users_id_seq'::regclass), username TEXT NOT NULL, email TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, status TEXT NOT NULL DEFAULT 'active', CONSTRAINT users_pkey PRIMARY KEY (id), CONSTRAINT users_username_key UNIQUE (username), CONSTRAINT users_email_key UNIQUE (email), CONSTRAINT check_email_format CHECK (email ~* '^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$'), CONSTRAINT check_status_values CHECK (status IN ('active', 'inactive', 'suspended')));COMMENT ON TABLE public.users IS 'Application users';COMMENT ON COLUMN public.users.email IS 'User email address (unique)';CREATE INDEX idx_users_email ON public.users(email);CREATE INDEX idx_users_created_at ON public.users(created_at);CREATE INDEX idx_users_status ON public.users(status) WHERE status != 'inactive';-- Owned sequence for id columnCREATE SEQUENCE public.users_id_seq;ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id;
CREATE VIEW public.active_users ASSELECT id, username, email, created_atFROM public.usersWHERE status = 'active';COMMENT ON VIEW public.active_users IS 'View of all active users';
CREATE FUNCTION public.get_user_count()RETURNS INTEGERLANGUAGE sqlAS $$ SELECT COUNT(*) FROM public.users;$$;COMMENT ON FUNCTION public.get_user_count() IS 'Returns total count of users';
-- Independent sequences (not owned by any table column)CREATE SEQUENCE public.custom_id_seq START WITH 1000 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;COMMENT ON SEQUENCE public.custom_id_seq IS 'Custom sequence for special IDs';