diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt index 48ded29b41..fe716ad891 100644 --- a/doc/sql.extensions/README.ddl.txt +++ b/doc/sql.extensions/README.ddl.txt @@ -158,6 +158,7 @@ basic_type: - PACKAGE - USER (ability to store comment depends upon user management plugin) - SECURITY CLASS (not implemented because Borland hid them). +- [GLOBAL] MAPPING 5) Allow setting and dropping default values from table fields. diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 6cb8316c99..05893c7686 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -10500,6 +10500,7 @@ string MappingNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, fromType); NODE_PRINT(printer, from); NODE_PRINT(printer, to); + NODE_PRINT(printer, comment); NODE_PRINT(printer, op); NODE_PRINT(printer, mode); NODE_PRINT(printer, global); @@ -10558,10 +10559,22 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) case MAP_RPL: ddl = "CREATE OR ALTER MAPPING "; break; + case MAP_COMMENT: + ddl = "COMMENT ON MAPPING "; + break; + } + addItem(ddl, name.c_str()); + + if (op == MAP_COMMENT) + { + ddl += " IS "; + if (comment) + addItem(ddl, comment->c_str(), '\''); + else + ddl += "NULL"; } - addItem(ddl, name.c_str()); - if (op != MAP_DROP) + else if (op != MAP_DROP) { ddl += " USING "; switch (mode) @@ -10654,6 +10667,7 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) (Arg::Gds(isc_map_already_exists) << name).raise(); break; + case MAP_COMMENT: case MAP_MOD: case MAP_DROP: if (!hasLine) @@ -10676,6 +10690,10 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) Field f2(full, 255); Field nm2(full, MAX_SQL_IDENTIFIER_LEN); + Message cmnt; + Field c3(cmnt, MAX_VARY_COLUMN_SIZE); + Field nm3(cmnt, MAX_SQL_IDENTIFIER_LEN); + toType = role ? 1 : 0; if (to) t = to->c_str(); @@ -10688,7 +10706,9 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) type2 = fromType->c_str(); if (from) f2 = fromUtf8.c_str(); - nm2 = name.c_str(); + if (comment) + c3 = comment->c_str(); + nm3 = nm2 = name.c_str(); Message* msg = NULL; const char* sql = NULL; @@ -10707,6 +10727,11 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext) "where RDB$MAP_NAME = ?"; msg = &full; break; + case MAP_COMMENT: + sql = "update RDB$AUTH_MAPPING set RDB$DESCRIPTION = ? " + "where RDB$MAP_NAME = ?"; + msg = &cmnt; + break; case MAP_DROP: sql = "delete from RDBAUTH_MAPPING where RDB$MAP_NAME = ?"; msg = &msgCheck; @@ -10751,7 +10776,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); - fb_assert(op == MAP_DROP || fromType); + fb_assert(op == MAP_DROP || op == MAP_COMMENT || fromType); short plugNull = plugin ? FALSE : TRUE; short dbNull = db ? FALSE : TRUE; @@ -10807,6 +10832,17 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd END_MODIFY break; + case MAP_COMMENT: + MODIFY M + M.RDB$DESCRIPTION.NULL = !(comment && comment->hasData()); + if (!M.RDB$DESCRIPTION.NULL) + { + AutoBlb b(tdbb, blb::create(tdbb, transaction, &M.RDB$DESCRIPTION)); + b->BLB_put_data(tdbb, (const UCHAR*)(comment->c_str()), comment->length()); + } + END_MODIFY + break; + case MAP_DROP: ddlTriggerAction = DDL_TRIGGER_DROP_MAPPING; executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, @@ -10870,16 +10906,18 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd case MAP_MOD: case MAP_DROP: + case MAP_COMMENT: if (!found) (Arg::Gds(isc_map_not_exists) << name).raise(); break; } - fb_assert(ddlTriggerAction > 0); + fb_assert(ddlTriggerAction > 0 || op == MAP_COMMENT); if (ddlTriggerAction > 0) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); - DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::MAPPING_CACHE); + if (op != MAP_COMMENT) + DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::MAPPING_CACHE); savePoint.release(); // everything is ok } diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 4686f94eab..a6752333f2 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -2066,7 +2066,7 @@ private: class MappingNode : public DdlNode, private ExecInSecurityDb { public: - enum OP {MAP_ADD, MAP_MOD, MAP_RPL, MAP_DROP}; + enum OP {MAP_ADD, MAP_MOD, MAP_RPL, MAP_DROP, MAP_COMMENT}; MappingNode(MemoryPool& p, OP o, const MetaName& nm) : DdlNode(p), @@ -2077,6 +2077,7 @@ public: fromType(NULL), from(NULL), to(NULL), + comment(NULL), op(o), mode('#'), global(false), @@ -2096,7 +2097,8 @@ protected: { statusVector << Firebird::Arg::Gds(isc_dsql_mapping_failed) << name << (op == MAP_ADD ? "CREATE" : op == MAP_MOD ? - "ALTER" : op == MAP_RPL ? "CREATE OR ALTER" : "DROP"); + "ALTER" : op == MAP_RPL ? "CREATE OR ALTER" : op == MAP_DROP ? + "DROP" : "COMMENT ON"); } void runInSecurityDb(SecDbContext* secDbContext); @@ -2111,6 +2113,7 @@ public: MetaName* fromType; IntlString* from; MetaName* to; + Firebird::string* comment; OP op; char mode; // * - any source, P - plugin, M - mapping, S - any serverwide plugin bool global; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 7059de994a..e2e04f8295 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -5651,6 +5651,8 @@ comment { $$ = newNode($3, *$4, "", *$6); } | comment_on_user { $$ = $1; } + | comment_on_mapping + { $$ = $1; } ; %type comment_on_user @@ -7307,6 +7309,28 @@ drop_map_clause($global) } ; +%type comment_on_mapping +comment_on_mapping + : COMMENT ON MAPPING map_comment(false) + { + $$ = $4; + } + | COMMENT ON GLOBAL MAPPING map_comment(true) + { + $$ = $5; + } + ; + +%type map_comment() +map_comment($global) + : map_name IS ddl_desc + { + $$ = newNode(MappingNode::MAP_COMMENT, *$1); + $$->global = $global; + $$->comment = $3; + } + ; + %type map_clause() map_clause($op) : map_name diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index 003f18c476..19bf57a8ae 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -1725,10 +1725,11 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) Field from(mMap, 255); Field role(mMap); Field to(mMap, MAX_SQL_IDENTIFIER_SIZE); + Field desc(mMap); curs = att->openCursor(&st, tra, 0, "SELECT RDB$MAP_NAME, RDB$MAP_USING, RDB$MAP_PLUGIN, RDB$MAP_DB, " - " RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_TO_TYPE, RDB$MAP_TO " + " RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_TO_TYPE, RDB$MAP_TO, RDB$DESCRIPTION " "FROM RDB$AUTH_MAPPING", 3, nullptr, nullptr, mMap.getMetadata(), nullptr, 0); if (st->getState() & IStatus::STATE_ERRORS) @@ -1798,6 +1799,20 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) DumpField(f_sec_map_to, VALUE_STRING, to->len, to->data)); } + if (!desc.null) + { + RefPtr blb(REF_NO_INCR, att->openBlob(&st, tra, &desc, 0, nullptr)); + check("IAttachment::openBlob", &st); + string buf; + const FB_SIZE_T FLD_LIMIT = MAX_COLUMN_SIZE; + unsigned length = 0; + blb->getSegment(&st, FLD_LIMIT, buf.getBuffer(FLD_LIMIT), &length); + check("IBlob::getSegment", &st); + + putField(tdbb, record, + DumpField(f_sec_map_comment, VALUE_STRING, length, buf.c_str())); + } + buffer->store(record); } check("IResultSet::fetchNext", &st); diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 1016ad7c8e..d9e1b03110 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -685,6 +685,7 @@ RELATION(nam_sec_global_auth_mapping, rel_global_auth_mapping, ODS_12_0, rel_vir FIELD(f_sec_map_from, nam_sec_map_from, fld_map_from, 0, ODS_12_0) FIELD(f_sec_map_to_type, nam_sec_map_to_type, fld_obj_type, 0, ODS_12_0) FIELD(f_sec_map_to, nam_sec_map_to, fld_map_to, 0, ODS_12_0) + FIELD(f_sec_map_comment, nam_sec_description, fld_description, 0, ODS_13_1) END_RELATION // Relation 47 (RDB$DB_CREATORS)