@@ -433,19 +433,46 @@ declare function setup:suppress-comments($nodes) {
433433 default return $node
434434};
435435
436+ declare function setup:unique-attributes ($attrs) {
437+ let $result := map:map ()
438+ let $_ :=
439+ for $attr in $attrs
440+ let $attr-name := fn:name ($attr)
441+ let $existing-attr := map:get ($result, $attr-name)
442+ return
443+ if (fn:exists ($existing-attr) and fn:string ($existing-attr) != fn:string ($attr)) then
444+ fn:error (
445+ xs:QName ("ATTR-CONFLICT" ),
446+ fn:concat ("The config-files contain multiple attributes " , $attr-name, " with different values: " || fn:string ($existing-attr) || " vs " || fn:string ($attr))
447+ )
448+ else
449+ map:put ($result, name ($attr), $attr)
450+ return map:keys ($result) ! map:get ($result, .)
451+ };
452+
436453(: for backwards-compatibility :)
437- declare function setup:rewrite-config ($import-configs as element (configuration )+) as element (configuration)
454+ declare function setup:rewrite-config ($import-configs as node ( )+) as element (configuration)
438455{
439456 setup:rewrite-config ($import-configs, ())
440457};
441458
442- declare function setup:rewrite-config ($import-configs as element (configuration)+, $silent as xs:boolean?) as element (configuration)
459+ declare function setup:rewrite-config ($import-configs as node ()+, $silent as xs:boolean?) as element (configuration)
460+ {
461+ setup:rewrite-config ($import-configs, $silent, ())
462+ };
463+
464+ declare function setup:rewrite-config ($import-configs as node ()+, $silent as xs:boolean?, $keep-comments as xs:boolean?) as element (configuration)
443465{
444466 let $config :=
445467 element { fn:node-name ($import-configs[1 ]) } {
446- $import-configs/@*,
468+ setup:unique-attributes ( $import-configs/@*) ,
447469
448- <groups xmlns = "http://marklogic.com/xdmp/group" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://marklogic.com/xdmp/group group.xsd" >{
470+ (: capture comments before gr:groups, and its older counterparts :)
471+ $import-configs/(
472+ gr:groups, gr:http-servers, gr:xdbc-servers, gr:odbc-servers, gr:task-server
473+ )/preceding-sibling::node (),
474+
475+ <groups xmlns = "http://marklogic.com/xdmp/group" >{
449476 let $default-group := ($import-configs/@default-group , "Default" )[1 ]
450477 for $group in fn:distinct-values (
451478 ($import-configs/gr:groups/gr:group /gr:group-name, $import-configs/(gr:http-servers/gr:http-server, gr:xdbc-servers/gr:xdbc-server,
@@ -460,8 +487,10 @@ declare function setup:rewrite-config($import-configs as element(configuration)+
460487 where fn:exists ($servers | $databases | $group-config)
461488 return
462489 <group>
490+ { setup:unique-attributes ($group-config/@*) }
463491 <group-name>{$group}</group-name>
464492 {
493+ $group-config/(node () except (gr:group-name, gr:http-servers, gr:xdbc-servers, gr:odbc-servers, gr:task-server)),
465494 if ($http-servers) then
466495 <http-servers>{$http-servers}</http-servers>
467496 else (),
@@ -473,13 +502,20 @@ declare function setup:rewrite-config($import-configs as element(configuration)+
473502 else (),
474503 if ($task-server) then
475504 $task-server
476- else (),
477- $group-config/(node () except gr:group-name)
505+ else ()
478506 }
479507 </group>
480508 }</groups>,
481509
482- $import-configs/(node () except (gr:groups, gr:http-servers, gr:xdbc-servers, gr:odbc-servers, gr:task-server))
510+ (: capture anything following gr:groups, and its older counterparts :)
511+ $import-configs/(
512+ gr:groups, gr:http-servers, gr:xdbc-servers, gr:odbc-servers, gr:task-server
513+ )/following-sibling::node (),
514+
515+ (: other fragments with configuration as root :)
516+ $import-configs[fn:empty ((
517+ gr:groups, gr:http-servers, gr:xdbc-servers, gr:odbc-servers, gr:task-server
518+ ))]/node ()
483519 }
484520
485521 (: Check config on group consistency! :)
@@ -492,10 +528,11 @@ declare function setup:rewrite-config($import-configs as element(configuration)+
492528 return
493529 fn:error (
494530 xs:QName ("NO_HOSTS_IN_GROUP" ),
495- fn:concat ("No hosts assigned to group " , $group, ", needed for app servers and forests!" ))
531+ fn:concat ("No hosts assigned to group " , $group, ", needed for app servers and forests!" )
532+ )
496533
497534 (: all good :)
498- return setup:suppress-comments ($config)
535+ return if ($keep-comments) then $config else setup:suppress-comments ($config)
499536};
500537
501538
@@ -544,7 +581,7 @@ declare private function setup:parse-options( $options as xs:string ) as map:map
544581 return $optionsMap
545582};
546583
547- declare function setup:do-setup ($import-config as element (configuration )+, $options as xs:string) as item ()*
584+ declare function setup:do-setup ($import-config as node ( )+, $options as xs:string) as item ()*
548585{
549586 let $optionsMap := setup:parse-options ( $options )
550587 let $do-internals := map:contains ( $optionsMap, "internals" )
@@ -611,7 +648,7 @@ declare function setup:do-setup($import-config as element(configuration)+, $opti
611648 }
612649};
613650
614- declare function setup:do-wipe ($import-config as element (configuration )+, $options as xs:string) as item ()*
651+ declare function setup:do-wipe ($import-config as node ( )+, $options as xs:string) as item ()*
615652{
616653 let $options := if (fn:empty ($options) or $options eq "" ) then ("all" ) else fn:tokenize ($options, "," )
617654
@@ -1038,7 +1075,7 @@ declare function setup:do-wipe($import-config as element(configuration)+, $optio
10381075 Attempt to remove replicas that are to be decommissioned due to scaling out of the cluster.
10391076 Replicas are only removed after their new replacements have gone to sync replication.
10401077:)
1041- declare function setup:do-clean-replicas ($import-config as element (configuration )+, $options as xs:string) as item ()*
1078+ declare function setup:do-clean-replicas ($import-config as node ( )+, $options as xs:string) as item ()*
10421079{
10431080 let $optionsMap := setup:parse-options ( $options )
10441081 let $do-internals := map:contains ( $optionsMap, "internals" )
@@ -1119,7 +1156,7 @@ declare function setup:do-clean-replicas($import-config as element(configuration
11191156(:
11201157 Cleanup scale-out replica state files.
11211158:)
1122- declare function setup:do-clean-replicas-state ($import-config as element (configuration )+, $options as xs:string) as item ()*
1159+ declare function setup:do-clean-replicas-state ($import-config as node ( )+, $options as xs:string) as item ()*
11231160{
11241161 let $optionsMap := setup:parse-options ( $options )
11251162 let $do-internals := map:contains ( $optionsMap, "internals" )
0 commit comments