// file : odb/relational/pgsql/schema.cxx // copyright : Copyright (c) 2009-2017 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include #include using namespace std; namespace relational { namespace pgsql { namespace schema { namespace relational = relational::schema; using relational::table_set; // // Drop. // struct drop_table: relational::drop_table, context { drop_table (base const& x): base (x) {} virtual void traverse (sema_rel::table& t, bool migration) { // For migration drop foreign keys explicitly in pre-migration. // if (migration) { base::traverse (t, migration); return; } // For schema creation we use the CASCADE clause to drop foreign // keys. // if (pass_ != 2) return; pre_statement (); os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") << quote_id (t.name ()) << " CASCADE" << endl; post_statement (); } }; entry drop_table_; // // Create. // struct create_column: relational::create_column, context { create_column (base const& x): base (x) {} virtual void type (sema_rel::column& c, bool auto_) { if (auto_) { // This should never fail since we have already parsed this. // sql_type const& t (parse_sql_type (c.type ())); // The model creation code makes sure it is one of these type. // if (t.type == sql_type::INTEGER) os << "SERIAL"; else if (t.type == sql_type::BIGINT) os << "BIGSERIAL"; } else base::type (c, auto_); } }; entry create_column_; struct create_foreign_key: relational::create_foreign_key, context { create_foreign_key (base const& x): base (x) {} virtual void deferrable (sema_rel::deferrable d) { os << endl << " INITIALLY " << d; } }; entry create_foreign_key_; struct create_index: relational::create_index, context { create_index (base const& x): base (x) {} virtual void create (sema_rel::index& in) { os << "CREATE "; if (!in.type ().empty ()) { // Handle the CONCURRENTLY keyword. // string const& t (in.type ()); if (t == "concurrently" || t == "CONCURRENTLY") { os << "INDEX " << t; } else { size_t p (t.rfind (' ')); string s (t, (p != string::npos ? p + 1 : 0), string::npos); if (s == "concurrently" || s == "CONCURRENTLY") os << string (t, 0, p) << " INDEX " << s; else os << t << " INDEX"; } } else os << "INDEX"; os << " " << name (in) << endl << " ON " << table_name (in); if (!in.method ().empty ()) os << " USING " << in.method (); os << " ("; columns (in); os << ")" << endl; if (!in.options ().empty ()) os << ' ' << in.options () << endl; } }; entry create_index_; // // Alter. // struct alter_column: relational::alter_column, context { alter_column (base const& x): base (x) {} virtual void alter (sema_rel::column& c) { os << quote_id (c.name ()) << " " << (c.null () ? "DROP" : "SET") << " NOT NULL"; } }; entry alter_column_; // // Schema version table. // struct version_table: relational::version_table, context { version_table (base const& x): base (x) {} // PostgreSQL prior to 9.1 doesn't support IF NOT EXISTS in // CREATE TABLE. We also cannot use IF-ELSE construct in plain // SQL. To make it at least work for a single schema, we are // going to drop the schema version table after the DROP // statements and then unconditionally create it after CREATE. // virtual void create_table () { if (options.pgsql_server_version () >= pgsql_version (9, 1)) { pre_statement (); os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl << " " << qv_ << " BIGINT NOT NULL," << endl << " " << qm_ << " BOOLEAN NOT NULL)" << endl; post_statement (); } } virtual void drop () { pre_statement (); if (options.pgsql_server_version () >= pgsql_version (9, 1)) os << "DELETE FROM " << qt_ << endl << " WHERE " << qn_ << " = " << qs_ << endl; else os << "DROP TABLE IF EXISTS " << qt_ << endl; post_statement (); } virtual void create (sema_rel::version v) { pre_statement (); if (options.pgsql_server_version () >= pgsql_version (9, 1)) { os << "INSERT INTO " << qt_ << " (" << endl << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl << " SELECT " << qs_ << ", " << v << ", FALSE" << endl << " WHERE NOT EXISTS (" << endl << " SELECT 1 FROM " << qt_ << " WHERE " << qn_ << " = " << qs_ << ")" << endl; } else { os << "CREATE TABLE " << qt_ << " (" << endl << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl << " " << qv_ << " BIGINT NOT NULL," << endl << " " << qm_ << " BOOLEAN NOT NULL)" << endl; post_statement (); pre_statement (); os << "INSERT INTO " << qt_ << " (" << endl << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl << " VALUES (" << qs_ << ", " << v << ", FALSE)" << endl; } post_statement (); } virtual void migrate_pre (sema_rel::version v) { pre_statement (); os << "UPDATE " << qt_ << endl << " SET " << qv_ << " = " << v << ", " << qm_ << " = TRUE" << endl << " WHERE " << qn_ << " = " << qs_ << endl; post_statement (); } virtual void migrate_post () { pre_statement (); os << "UPDATE " << qt_ << endl << " SET " << qm_ << " = FALSE" << endl << " WHERE " << qn_ << " = " << qs_ << endl; post_statement (); } }; entry version_table_; } } }