{
  "date": "2018-06-01T10:36:09",
  "slug": "sqlcl-connect-target-depends-on-previous-connection",
  "link": "https://www.dbi-services.com/blog/sqlcl-connect-target-depends-on-previous-connection/",
  "title": {
    "rendered": "SQLcl connect target depends on previous connection"
  },
  "content": {
    "rendered": "<h2>By Franck Pachot</h2>\n<p>.<br />\nI thought it was a bug for two reasons: first, because I don&#8217;t like that my connect destination depends on the context, and then because it is a different behavior than in SQL*Plus. But finally, it is the correct behavior, and the 100% compatibility with SQL*Plus is expected only when SET CLASSIC=ON. And as I was surprised, and I think it can be dangerous, here is a blog post about it. Basically, be very careful if you are connected with a service name and you want to connect locally.<br />\n<!--more--><br />\nBasically, if you attended my demo about &#8216;From Transportable Tablespaces to Pluggable Databases&#8217;, where I switch between different instances you may have seen that I had to run &#8216;connect / as sysdba&#8217; two times because the first one failed with invalid username/password</p>\n<pre><code>\nSQL&gt; connect scott/tiger@//localhost/PDB1\nConnected.\n...\nSQL&gt; connect / as sysdba\nUSER =\nURL = jdbc:oracle:oci8:@//localhost/PDB1\nError Message = ORA-01017: invalid username/password; logon denied\n&nbsp;\nWarning: You are no longer connected to ORACLE.\n&nbsp;\nSQL&gt; connect / as sysdba\nConnected.\n</code></pre>\n<p>This is not a big deal, but that means that it tries to connect to //localhost/PDB1 when I wanted to connect locally to my ORACLE_SID environment variable. Here, expecting a bequeath connection, I didn&#8217;t provide a password, then I cannot connect to the PDB. But imagine that I use a password, and the password is the same in the two databases&#8230; I would have been connected to the wrong database. Just imagine this:</p>\n<pre><code>\nSQL&gt; host echo $ORACLE_SID\nTEST\nSQL&gt; connect sys/password as sysdba\nConnected.\n-- checking something on PROD\nSQL&gt; connect sys/password@PROD as sysdba\nConnected.\n...\n-- back to TEST (or at least I think so)\nSQL&gt; host echo $ORACLE_SID\nTEST\nSQL&gt; connect sys/password as sysdba\nConnected.\n-- do terrible things\nSQL&gt; drop table VERY_IMPORTANT_TABLE;\nTable VERY_IMPORTANT_TABLE dropped.\n-- now look where I am:\nSQL&gt; show connection\nCONNECTION:\nSYS@jdbc:oracle:oci8:@PROD AS SYSDBA\nCONNECTION_IDENTIFIER:\nPROD\n</code></pre>\n<p>Actually, what happens is that when SQLcl is already connected with a connection string (i.e not locally using bequeath) the next connect command will use the same connection string. This means that:</p>\n<pre><code>\nconnect user/password\n</code></pre>\n<p>is actually equivalent to</p>\n<pre><code>\nconnect user/password@&amp;_CONNECT_IDENTIFIER\n</code></pre>\n<h3>SQL*Plus</h3>\n<p>This behavior has been introduced in SQLcl but this is not how SQL*Plus works:</p>\n<pre><code>\nSQL&gt; connect sys/oracle@//localhost/PDB1 as sysdba\nConnected.\nSQL&gt; connect / as sysdba\nConnected.\nSQL&gt; define _CONNECT_IDENTIFIER\nDEFINE _CONNECT_IDENTIFIER = \"CDB1\" (CHAR)\n</code></pre>\n<h3>Disconnect</h3>\n<p>The first solution to avoid this in SQLcl is to always disconnect before you want to connect to a different service:</p>\n<pre><code>\nSQL&gt; connect sys/oracle@//localhost/PDB1 as sysdba\nConnected.\nSQL&gt; disc\n&nbsp;\nDisconnected from Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production\nVersion 18.2.0.0.0\nSQL&gt; connect sys/oracle as sysdba\nConnected.\nSQL&gt; show connection\nCONNECTION:\nSYS@jdbc:oracle:oci8:@ AS SYSDBA\nCONNECTION_IDENTIFIER:\nCDB$ROOT\n</code></pre>\n<p>This is why the second time was ok in my case: first one failed with invalid password and then I was disconnected.</p>\n<h3>TWO_TASK</h3>\n<p>The second solution is to set an impossible TWO_TASK (or LOCAL in Windows) so that local connections are impossible:</p>\n<pre><code>\nSQL&gt; connect sys/oracle@//localhost/PDB1 as sysdba\nConnected.\nSQL&gt; connect sys/oracle as sysdba\nUSER = sys\nURL = jdbc:oracle:oci8:@NoWhere\nError Message = ORA-12154: TNS:could not resolve the connect identifier specified\nUSER = sys\nURL = jdbc:oracle:thin:@NoWhere\nError Message = IO Error: Unknown host specified\nUSER = sys\nURL = jdbc:oracle:thin:@NoWhere:1521/NoWhere\nError Message = IO Error: Unknown host specified\n</code></pre>\n<h3>CLASSIC=ON</h3>\n<p>The third solution is to run SQLcl in SQL*Plus 100% compatible mode:</p>\n<pre><code>\nSQL&gt; connect sys/oracle@//localhost/PDB1 as sysdba\nConnected.\nSQL&gt; set classic on\nSQL&gt; show classic\nSQL*Plus mode: ON\nSQL&gt; connect / as sysdba\nConnected.\n</code></pre>\n<p>Here we have the same behavior as SQL*Plus: no use of current connection string.</p>\n<p>The SQL CLASSIC ON is usually for the output (error messages, autotrace statistics, and a few enhancement made to SQLcl). And the online help still says that it is about output:</p>\n<pre><code>\nSQL&gt; help set classic\nSET CLASSIC\nSET CLASSIC [ ON | OFF ]\nSet classic SQL*Plus Settings on\nThis will allow scripts which expect traditional output to be honored.\n</code></pre>\n<p>However, it seems that this CLASSIC mode is also very important for connection.</p>\n<h3>Test and show _CONNECTION_STRING</h3>\n<p>If you show the connection string at the prompt, this may prevent errors:</p>\n<pre><code>\nSQL&gt; set sqlprompt \"_connect_identifier&gt; \"\n//localhost/CDB2&gt; connect sys/oracle@//localhost/CDB1 as sysdba\nConnected.\n//localhost/CDB1&gt; connect sys/oracle as sysdba\nConnected.\n</code></pre>\n<h3>Always check which database</h3>\n<p>By the way, when I prepare a script that can make some damages when not run at the correct place, I usually add a test on DBID on top of it:</p>\n<pre><code>\nCDB1&gt; whenever sqlerror exit failure;\nCDB1&gt; select 0/decode(dbid,'944121612',1,0) from v$database;\n&nbsp;\nError starting at line : 1 in command -\nselect 0/decode(dbid,'944121612',1,0) from v$database\nError report -\nORA-01476: divisor is equal to zero\n</code></pre>\n<h3>Different passwords</h3>\n<p>Of course, you should have different passwords on prod and test databases. However, I prefer to have passwords in a wallet (external password file) and then you will always have the correct identification as it is recorded for each service name.</p>\n",
    "protected": false
  }
}
