Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vvHQe-00BmYW-1E for pgsql-bugs@arkaria.postgresql.org; Wed, 25 Feb 2026 16:08:00 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vvHQd-0078RR-1D for pgsql-bugs@arkaria.postgresql.org; Wed, 25 Feb 2026 16:07:59 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vvHQd-0078RI-01 for pgsql-bugs@lists.postgresql.org; Wed, 25 Feb 2026 16:07:59 +0000 Received: from mail-yw1-x112a.google.com ([2607:f8b0:4864:20::112a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vvHQZ-00000001F9X-0PJW for pgsql-bugs@lists.postgresql.org; Wed, 25 Feb 2026 16:07:57 +0000 Received: by mail-yw1-x112a.google.com with SMTP id 00721157ae682-797ab169454so67599297b3.3 for ; Wed, 25 Feb 2026 08:07:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1772035673; cv=none; d=google.com; s=arc-20240605; b=dk6yJkYYy9fn4RamWqHGJqZRLoVa1b7bthOLYemxTLtKRdnTZ+oBkixhk1LS60HhHL mOMDB59hnT/nDNbkE3vM/D40covaj++vHkSaLMkIu78EciqfZY8Y6EuduFonq+yMpBfp 4NHToujQVBj9YeWCtPPGQ3cUgIX4CICjX6iaj5em8U97Kpifd7plP121JBe8uzDm96sr eSCyGh9aVzroTuU1ObYajVt/rSze3ewNKYf/KYZw/diKhp27jmKxGoZYnPrwzwI/qn6G emyjsjYHoL1xuI7Y+8cwISyFwbTL+MXb7WVuHavjSwG9uFa5daFJxYnW7RetDa4CiqY3 xkSw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=vRqeXdofBYiNkJpOVMNQdLCDgwdi/diS9j1PpypY5rU=; fh=to06lfO9m4SBKDmTfBK1twJI0dddYJrj3tbyEOGcl60=; b=HhUKuBPaSDgIBswC9by37H1Gb0wuRnIsdb8pOkk4vlAGt21003PrnS/hK5mqCtOyMV K175u71A28D2+q/3VEGc4gyyAWsU6WXtEv1HIa1dhWThUMqA5J1lK4sTBCeqpYtk58Wa JTB0B5G3SCaq3KjXIWg2xWXUTGMMc4bec4uexje48Rj/FiNcxPDPj4AtrBiP38RUIiol GK+HRKMLCa6es0HuDKFfdpV/fL01qWhV/3YoLhC9KWAOIPSI0TuLZVdMMYJlAO38gS55 +2vpXDjC6haaPiBfzdMc0S0hQ9o5CipVC9gU03iFgEVuUnzGB5vezUZ5bRBhS/yqVHyO G8TQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=enterprisedb.com; s=google; t=1772035673; x=1772640473; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=vRqeXdofBYiNkJpOVMNQdLCDgwdi/diS9j1PpypY5rU=; b=R/mtgQ63zKgDLttsCxjN2n9hOLGdr5m/v5wa1W7lShYD9I3DvCHWhMNnzSQodRgeoF jWYrbLe9A8Uc4ZvZqVLy4hsAP1XUTG8GXt8O2n9+93CfB8QWxcMoZVZ7MwDPSWdQbPSF LgNFyItTQWqnPq4/gbh6Pt3h6PuzIHMljekwvazRILAxW7pUExNzCoScWSKfoZEmZuHT xPJer6EvD/0pGNPAWTw9BNSlz9WSSWwcIAadru+BwLFnDcpufHYNtGME4CZfvojQLjn2 BV1+PfHVAGexbhaRheuzcAShdPq2BqLarmsm0sOdJ/K0R8gkFLrDb7Apj32ILywjNXC1 1KPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772035673; x=1772640473; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=vRqeXdofBYiNkJpOVMNQdLCDgwdi/diS9j1PpypY5rU=; b=Rfart16+hAEoXZnw3JSf0b60o/fsiSlfz9BpHuBdc4oagxFpcZZTHLidds/tUzDkTh WiW2ie1a93YsEZo1Cfn/Zk39J2sD2yU+Eds9ow68u2F+HZDoqLlnFwOWO6vSUxA8zILl Bsppqw5OCS05DAcmGwgN0HeZqXAc7CJszZ23zyzRoJvUk5HjPhTSEm0r3VXs+dwUd9C7 0ZPTF0NLSC9/X7cFNvNpEiH+qwaXLmEC0kjjeGGx66dCXW+DV5RWXgkzUq33XYUZgcIj chmgG7l47sRiLz8auwkO+SvHxSMC7NkmW7q7itoAyKi8zHOnHrj7Vn969CtBmiJaJBpt 1SFw== X-Gm-Message-State: AOJu0YxwVx/MNZX1cQaag1oOusI0rVM4juXe+K7c8BroXDBhdWe1bYoB IJMKDwJZsKhHBeYEYNMWLqjReX5hj0RTmAo1WynpubgMFpqY0YnGV3amsn/pjfnCZRE/dYph1dC G07Ziq73rhZLpl06dyijkd69jkr07prkbsgVUhzpA X-Gm-Gg: ATEYQzyNuNzLytH87FDJqVM0T8wb55W96ojZEjHpf77xhHhmc65mN5q/INdE6o8a3NG VR66bPrX13CvQlMMrC+BgEM3Jj85tbq9RjHVNC15di0+TGlbQP1SPH0jAXOYpGWgGqNMilh+1UU H0arl8uYlY6an48Eh7Fx66FNWcnmjQtyiW4+x35YXQqd4bWh2/EDb1WEeQR+gY/irWwvy4Q6BdQ 8TVcpT2Av8xnWKyAd5B9WDypyU4TmbdyXUwOGRGKLbWIBCwaliMdUpjFznNaog0l/x2oti55eKf CpYY4HTYgAuqNoc8Iz0= X-Received: by 2002:a05:690c:c4f9:b0:786:506c:3cc4 with SMTP id 00721157ae682-7986fe35b0fmr10337287b3.53.1772035673075; Wed, 25 Feb 2026 08:07:53 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Sandeep Thakkar Date: Wed, 25 Feb 2026 21:37:36 +0530 X-Gm-Features: AaiRm517rHt-jCY74YUdx7mQDoKHRIGDpqxwaE9pdrEqElKwX4UFr2ykBhuB8Ck Message-ID: Subject: Re: PostgreSQL MSI ignores --datadir flag during minor upgrade on Windows To: Ben Caspi Cc: pgsql-bugs@lists.postgresql.org, Avi Uziel , Liran Amrani , Shahar Amram Content-Type: multipart/alternative; boundary="000000000000d2c81d064ba834a2" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000d2c81d064ba834a2 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, Feb 20, 2026 at 1:51=E2=80=AFPM Ben Caspi wrote: > Hello, > > Recently we tried to upgrade a client machine's PostgreSQL version from > 15.13 to 15.16. > We often upgrade our client machines from versions 9/13 to 15 without > encountering any issues. > However, during this upgrade (15.13 --> 15.16) we've noticed that > post-upgrade the PostgreSQL service assigned data directory changed to th= e > default value "C:\Program Files\PostgreSQL\15\data". > > Our client's data directory prior to the upgrade was > "F:\AidocData\PostgreSQL\15\data". This change caused data to be written = to > the wrong location until we found the issue. > > This is the command we use for the upgrade: > >> Start-Process $installer_exe -ArgumentList "--unattendedmodeui minimal >> --mode unattended --superaccount $POSTGRES_ROOT_USER --superpassword >> $POSTGRES_ROOT_USER_PWD --serverport $port --datadir `"$data_dir`" --loc= ale >> `"$locale_name`"" -Wait > > > I looked into your codebase and found that during an upgrade the installe= r > defaults to the Registry value for data directory and ignores the --datad= ir > flag I provided in my command. > > That behaviour is by design. The command-line options override the defaul= t values only during the new installation. In upgrade mode, the registry values are the source of truth for the installer. In server/pgserver.xml.in, in the server component=E2=80=99s > preInstallationActionList: > > *pgserver.xml.in * > Lines 1142-1153 > > >> >> datadir >> ${iDataDirectory} >> >> >> ${iDataDirectory} >> not_empty >> >> >> >> > > > > *iDataDirectory is read from the registry here:* > *pgserver.xml.in * > Lines 1003-1008 > > >> >> Data Directory >> >> HKEY_LOCAL_MACHINE\SOFTWARE\PostgreSQL\Installa= tions\postgresql${service_suffix}-${product_version} >> iDataDirectory >> > > > *What goes wrong:* > When passing --datadir "F:\AidocData\PostgreSQL\15\data" on the command > line, the wizard sets datadir =3D "F:\AidocData\PostgreSQL\15\data". > > Later, in preInstallationActionList, the installer reads iDataDirectory > from the registry. > If iDataDirectory is not empty, it overwrites datadir with that value. > > 1. There is no check that the user explicitly provided --datadir, so > the CLI value is ignored. > > If the registry still has the old default path (e.g. C:\Program > Files\PostgreSQL\15\data), the service ends up re-registered with that pa= th > instead of F:\AidocData\PostgreSQL\15\data. > When the registry can be wrong > > - Initial install used the default data directory, then data was moved > to F:\AidocData\PostgreSQL\15\data and the service was updated manuall= y, > but the PostgreSQL registry key was not. > > This is the problem for the installer. If you change the paths later that were given during installation, you must also change the registry key value for the installer to know. > > - A previous install or reinstall wrote the default path to the > registry. > - Any other case where the registry value does not match the actual > data location. > > > *Fix* > Only use the registry value when the user has not provided --datadir on > the command line. For example, add a condition so that datadir is set fro= m > iDataDirectory only when datadir is empty: > > >> >> datadir >> ${iDataDirectory} >> >> >> ${iDataDirectory} >> not_empty >> >> >> ${datadir} >> empty >> >> >> >> > > > This keeps the current behavior when --datadir is not passed, but ensures > that an explicit --datadir is not overridden by the registry. > > Thanks, but this change in behaviour constitutes a design change and should probably also apply to other options. We'll evaluate this further. But for now, the fix is to update your registry keys to deflect the change you made to the datadir. > Please consider investigating this issue and its solution. > If I misunderstood anything please let me know. Thanks! > -- > > [image: photo] > > Ben Caspi > DevOps Engineer > > www.aidoc.com | benc@aidoc.com > > [image: linkedin] > > [image: twitter] > > [image: App Banner Image] > > > --=20 Sandeep Thakkar --000000000000d2c81d064ba834a2 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Fri, F= eb 20, 2026 at 1:51=E2=80=AFPM Ben Caspi <benc@aidoc.com> wrote:
Hello,
<= br>
Recently we tried to upgrade a client machine's PostgreSQ= L version from 15.13 to 15.16.
We often upgrade our client machin= es from versions 9/13 to 15 without encountering any issues.
Howe= ver, during this upgrade (15.13 --> 15.16) we've noticed that post-u= pgrade the PostgreSQL service assigned=C2=A0data directory changed to the d= efault value "C:\Program Files\PostgreSQL\15\data".
Our client's data directory prior to the upgrade was "= F:\AidocData\PostgreSQL\15\data". This change caused data to be writte= n to the=C2=A0wrong location until we found the issue.

=
This is the command we use for the upgrade:
Start-Pr= ocess $installer_exe -ArgumentList "--unattendedmodeui minimal --mode = unattended --superaccount $POSTGRES_ROOT_USER --superpassword $POSTGRES_ROO= T_USER_PWD --serverport $port --datadir `"$data_dir`" --locale `&= quot;$locale_name`"" -Wait

I lo= oked into your codebase and found that during an upgrade the installer defa= ults to the Registry value for data directory and ignores the --datadir fla= g I provided in my command.

That behaviour is by design. The command-line o= ptions override the default values only during the new installation. In upg= rade mode, the registry values are the source of truth for the installer.
In server/pgse= rver.xml.in, in the server component=E2=80=99s preInstallationActionLis= t:

pgserver.= xml.in
Lines 1142-1153

=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0<!-- Set datadir if an existing installation is found = -->
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<setInstallerVariable&g= t;
=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<na= me>datadir</name>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0<value>${iDataDirectory}</value>
=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<ruleList>
=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0<stringTest>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<= ;text>${iDataDirectory}</text>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0<type>not_empty</type>

=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0</stringTest>= ;
=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0<isFalse value=3D"${extract_mode}"/>
=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0</ruleLis= t>
= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0</setInstallerVariable&g= t;


iDataDirectory is read from the registry h= ere:
pgserve= r.xml.in
Lines 1003-1008

=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0<!-- Get the existing data directory. -->
=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0<registryGet>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<= ;name>Data Directory</name>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0<key>HKEY_LOCAL_MACHINE\SOFTWARE\PostgreSQL\Installations\postgres= ql${service_suffix}-${product_version}</key>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0<variable>iDataDirectory</variable>
=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0</registryGet>

What goes wrong:
When passing --datadir "F:\AidocData\PostgreSQL\15= \data" on the command line, the wizard sets datadir =3D "F:\Aidoc= Data\PostgreSQL\15\data".

Later, in preIns= tallationActionList, the installer reads iDataDirectory from the registry.<= br aria-hidden=3D"true">If iDataDirectory is not empty, it overwrites datad= ir with that value.
  1. There is no check that the user expl= icitly provided --datadir, so the CLI value is ignored.
If the registry still h= as the old default path (e.g. C:\Program Files\PostgreSQL\15\data), the ser= vice ends up re-registered with that path instead of F:\AidocData\PostgreSQ= L\15\data.
When the registry can be wrong
  • Initial install used the default data directory, then data was mo= ved to=C2=A0F:\AidocData\PostgreSQL\15\data=C2=A0and the service was update= d manually, but the PostgreSQL registry key was not.
<= /blockquote>
This is the problem for the installer. If you change the = paths later that were given during installation, you must also change the r= egistry key value for the installer to know.
=C2=A0
  • A previous install o= r reinstall wrote the default path to the registry.
  • Any other case where the registry= value does not match the actual data location.

Fix
Only use the registry value when the user has not provided --datadir on th= e command line. For example, add a condition so that datadir is set from iD= ataDirectory only when datadir is empty:

<!-- Set datadir if an existing installat= ion is found (only when user didn't provide --datadir) --><setInstalle= rVariable>
=C2=A0=C2=A0=C2=A0=C2=A0<name>datadir</name>
=C2=A0=C2=A0=C2= =A0=C2=A0<value>${iDataDirectory}</value>
=C2=A0=C2=A0=C2=A0=C2=A0<= ;ruleList>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<stringTest><= /span>
=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<text&= gt;${iDataDirectory}</text>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0<type>not_empty</type>
=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0</stringTest>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0<stringTest>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<text>${datadir}</text>=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<type>empty&= lt;/type>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0</stringTest><= /span>
=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<isFalse value=3D"${extra= ct_mode}"/>
=C2=A0=C2=A0=C2=A0=C2=A0</ruleList>
</setInstallerVariable= >

This keeps the current behavior when --datadir = is not passed, but ensures that an explicit --datadir is not overridden by = the registry.

Thanks, but this chan= ge in behaviour constitutes a design change and should probably also apply = to other options. We'll evaluate this further.
But for now, the fix = is to update your registry keys to deflect the change you made to the datad= ir.=C2=A0
=C2=A0
Please consider investigating this = issue and its solution.
If I misunderstood anything please let me= know. Thanks!
--
=

Ben Caspi
DevOps Engineer

=

www.aidoc.com=C2=A0=C2=A0|=C2=A0=C2=A0<= a href=3D"mailto:benc@aidoc.com" style=3D"font-family:Arial;text-decoration= :unset" rel=3D"nofollow noreferrer" target=3D"_blank">benc@aidoc.com

=
3D"linkedin"

3D"twitter"

=

3D"App


= =C2=A0


--
Sandeep Thakkar


--000000000000d2c81d064ba834a2--