// file : odb/relational/pgsql/schema.cxx // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include #include #include #include using namespace std; namespace relational { namespace pgsql { namespace schema { namespace relational = relational::schema; // // Drop. // struct drop_table: relational::drop_table, context { drop_table (base const& x): base (x) {} virtual void drop (sema_rel::qname const& table) { os << "DROP TABLE IF EXISTS " << quote_id (table) << " CASCADE" << endl; } }; entry drop_table_; // // Create. // struct create_foreign_key; struct create_table: relational::create_table, context { create_table (base const& x): base (x) {} void traverse (sema_rel::table&); private: friend class create_foreign_key; set tables_; // Set of tables we have already defined. }; entry create_table_; 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 ())); if (t.type == sql_type::INTEGER) os << "SERIAL"; else if (t.type == sql_type::BIGINT) os << "BIGSERIAL"; else { location const& l (c.get ("cxx-location")); error (l) << "automatically assigned object id must map " << "to PostgreSQL INTEGER or BIGINT" << endl; throw operation_failed (); } } else base::type (c, auto_); } }; entry create_column_; struct create_foreign_key: relational::create_foreign_key, context { create_foreign_key (schema_format f, relational::create_table& ct) : base (f, ct) { } create_foreign_key (base const& x): base (x) {} virtual void traverse (sema_rel::foreign_key& fk) { // If the referenced table has already been defined, do the // foreign key definition in the table definition. Otherwise // postpone it until pass 2 where we do it via ALTER TABLE // (see add_foreign_key below). // create_table& ct (static_cast (create_table_)); if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ()) { base::traverse (fk); fk.set ("pgsql-fk-defined", true); // Mark it as defined. } } virtual void deferred () { os << endl << " INITIALLY DEFERRED"; } }; entry create_foreign_key_; struct add_foreign_key: create_foreign_key, relational::common { add_foreign_key (schema_format f, relational::create_table& ct) : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ()) { } virtual void traverse (sema_rel::foreign_key& fk) { if (!fk.count ("pgsql-fk-defined")) { sema_rel::table& t (dynamic_cast (fk.scope ())); pre_statement (); os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl; base::create (fk); os << endl; post_statement (); } } }; void create_table:: traverse (sema_rel::table& t) { if (pass_ == 1) { tables_.insert (t.name ()); // Add it before to cover self-refs. base::traverse (t); return; } // Add foreign keys. // add_foreign_key fk (format_, *this); trav_rel::unames n (fk); names (t, n); } 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_; } } }