Changing the Authentication Mode in Oracle Primavera P6 EPPM Using SQL

Oracle Primavera P6 EPPM

I am currently in the process of setting up an Oracle Primavera P6 EPPM 18.8 environment. The operating system on the application and Web server is Windows Server 2012 R2, the middleware is Oracle WebLogic 12c, and Oracle Database 12c for the RDBMS.

This is the first time I’ve ever set up P6 from scratch, and I’m doing it without any assistance from Oracle, other than their documentation.

I’ve learned quite a lot in my adventure so far. One thing that I learned is that you REALLY don’t want to turn on LDAP Authentication everywhere until you’re sure it’s working.

(Relevant documentation: Configuring LDAP Settings in the Primavera P6 Administrator)

In the P6 Administration website, there are two places to change the type of authentication used: NATIVE, LDAP, and WebSSO. The Authentication tab:

P6 Administration screen - Authentication tab

And the authentication section on the database instance itself:

P6 Administration screen - Authentication section on DB instance

If both are set to “LDAP” (and presumably “WebSSO” as well) when things aren’t yet set up properly, you’ll get this lovely error: “Primavera P6 was configured to use a different authentication mode than the database selected”.

Google suggests that you read this Oracle doc to fix the problem. However, this problem is only fixed with this solution if only the DB instance setting is “LDAP”. If both are set that way, this solution does not fix the problem – though it should still be run so as to fix the setting on the DB instance page.

After poking around in the database, I discovered that the offending configuration setting was stored in a BLOB in the ADMIN_CONFIG table. Unfortunately, editing this BLOB is not easy without some assistance. Fortunately, I found an article on StackOverflow that had functions for converting a BLOB into a CLOB, at which point the REPLACE function could be used. Then convert the CLOB back to a BLOB, and UPDATE the database table.

First, create the two conversion functions:

CREATE OR REPLACE FUNCTION convert_to_clob(l_blob BLOB) RETURN CLOB IS
      l_clob         CLOB;
      l_dest_offset  NUMBER := 1;
      l_src_offset   NUMBER := 1;
      l_lang_context NUMBER := dbms_lob.default_lang_ctx;
      l_warning      NUMBER;
   BEGIN
      dbms_lob.createtemporary(l_clob, TRUE);
      dbms_lob.converttoclob(dest_lob     => l_clob,
                             src_blob     => l_blob,
                             amount       => dbms_lob.lobmaxsize,
                             dest_offset  => l_dest_offset,
                             src_offset   => l_src_offset,
                             blob_csid    => nls_charset_id('AL32UTF8'),
                             lang_context => l_lang_context,
                             warning      => l_warning);
      RETURN l_clob;
   END convert_to_clob;
   /
CREATE OR REPLACE FUNCTION convert_to_blob(l_clob CLOB) RETURN BLOB IS
      l_blob         BLOB;
      l_dest_offset  NUMBER := 1;
      l_src_offset   NUMBER := 1;
      l_lang_context NUMBER := dbms_lob.default_lang_ctx;
      l_warning      NUMBER;
   BEGIN
      dbms_lob.createtemporary(l_blob, TRUE);
      dbms_lob.converttoblob(dest_lob     => l_blob,
                             src_clob     => l_clob,
                             amount       => dbms_lob.lobmaxsize,
                             dest_offset  => l_dest_offset,
                             src_offset   => l_src_offset,
                             blob_csid    => nls_charset_id('AL32UTF8'),
                             lang_context => l_lang_context,
                             warning      => l_warning);
      RETURN l_blob;
   END convert_to_blob;
   /

Then run an UPDATE statement, something like this, changing the CONFIG_NAME appropriately:

UPDATE ADMIN_CONFIG
     SET CONFIG_DATA = convert_to_blob(
                          REPLACE(convert_to_clob(CONFIG_DATA),
                                 '<mode desc="SET_AUTHENTICATION_MODE" o1="NATIVE" o2="WebSSO" o3="LDAP" type="javax.swing.JComboBox">LDAP</mode>',
                                 '<mode desc="SET_AUTHENTICATION_MODE" o1="NATIVE" o2="WebSSO" o3="LDAP" type="javax.swing.JComboBox">NATIVE</mode>')
                          )
   WHERE CONFIG_NAME = 'myprimavera.bre.YourConfigName';
COMMIT;

After running this script and restarting the application servers, you should once again be back in NATIVE mode and able to get back into P6 to see what went wrong, and no longer crying over the website you thought you had bricked just a few minutes prior.

Enabling Integrated Windows Authentication in Chrome on a Mac

Google Chrome logo

I was surprised at how difficult it was to find this information, given that Chrome is certainly one of the most widely-used browsers in the world, and also that it is commonplace to have Macs connecting to Windows domains.

I found that the domains that would be sent IWA information are set in the AuthServerWhitelist policy. But how to change these policies?

After coming across this article, it appeared that changing the policy via the Terminal window would be the best – and possibly only – way to do this.

Enter the following line into Terminal, using comma-separated domains that you trust with your credentials (with or without wildcards), and press Enter.

$ defaults write com.google.Chrome AuthServerWhitelist "*.domain1.com, *.domain2.net, *.domain3.org"

Restart Chrome.

By accessing chrome://policy, now, you can see the new domains in the policy setting.

Chrome Policies page

While this has not proven to be foolproof – some domains still prompt for a user name and password – it seems to work more often than not.

SQL Server Script to Create INSERT Statements

Microsoft SQL Server logo

Though you can generate INSERT statements using SQL Server Management Services if the Generate Scripts functionality is enabled, this script may help in cases when it is not.

USE DatabaseName
GO
SET NOCOUNT ON

DECLARE @TableName VARCHAR(255)
DECLARE @IndividualInserts BIT

SET @TableName = 'TableName'
SET @IndividualInserts = 0

DECLARE @TableStructure TABLE (ColumnName VARCHAR(255), DataType VARCHAR(50), [MaxLength] INT, [precision] INT, [scale] INT, is_nullable BIT, PrimaryKey BIT, ColumnID INT)
DECLARE @ColumnName VARCHAR(255)
DECLARE @DataType VARCHAR(50)
DECLARE @MaxLength INT
DECLARE @precision INT
DECLARE @scale INT
DECLARE @is_nullable BIT
DECLARE @PrimaryKey BIT
DECLARE @ColumnID INT
DECLARE @SelectQuery VARCHAR(MAX) = ''
DECLARE @ResultQuery VARCHAR(MAX) = ''
DECLARE @SQL NVARCHAR(MAX) = ''
DECLARE @CrLf VARCHAR(10) = CHAR(13) + CHAR(10)

INSERT INTO @TableStructure
SELECT 
    c.name 'Column Name',
    t.Name 'Data type',
    c.max_length 'Max Length',
    c.precision ,
    c.scale ,
    c.is_nullable,
    ISNULL(i.is_primary_key, 0) 'Primary Key',
	c.column_id
FROM    
    sys.columns c
INNER JOIN 
    sys.types t ON c.user_type_id = t.user_type_id
LEFT OUTER JOIN 
    sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN 
    sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
WHERE
    c.object_id = OBJECT_ID(@TableName) 
ORDER BY c.column_id

--SELECT * FROM @TableStructure

DECLARE TableCursor CURSOR FOR
SELECT * FROM @TableStructure 
ORDER BY ColumnID


OPEN TableCursor
FETCH NEXT FROM TableCursor INTO @ColumnName, @DataType, @MaxLength, @precision, @scale, @is_nullable, @PrimaryKey, @ColumnID
WHILE @@FETCH_STATUS = 0
BEGIN
	--SELECT @ColumnName, @DataType, @MaxLength, @precision, @scale, @is_nullable, @PrimaryKey, @ColumnID
	SET @ColumnName = '[' + @ColumnName + ']'
	IF LEN(@SelectQuery)=0 SET @SelectQuery='SELECT' + @CrLf + '''INSERT INTO ' + @TableName + ' (' 
	ELSE SET @SelectQuery = @SelectQuery + ',' --@CrLf + ',' 
	SET @SelectQuery = @SelectQuery + @ColumnName -- + '   -- ' + @DataType

	IF LEN(@ResultQuery)=0 
	BEGIN
		IF @IndividualInserts=0 SET @ResultQuery = 'UNION ALL SELECT CASE WHEN ROW_NUMBER() OVER(ORDER BY ' + @ColumnName + ') &amp;amp;amp;lt;&amp;amp;amp;gt; 1 THEN '','' ELSE '''' END +' + @CrLf + '''('
		ELSE SET @ResultQuery= @CrLf + '(' 
	END
	ELSE SET @ResultQuery = @ResultQuery + ','
	SELECT @ResultQuery = 
	CASE
		WHEN @DataType LIKE '%CHAR' 
		THEN @ResultQuery + '''+ COALESCE('''''''' + ' + @ColumnName + ' + '''''''',''NULL'') +'''
		WHEN @DataType LIKE '%DATETIME' 
		THEN @ResultQuery + '''+ COALESCE(''CONVERT(DATETIME,'''''' + CONVERT(VARCHAR,' + @ColumnName + ',21) + '''''',21)'',''NULL'') +'''		
		WHEN @DataType LIKE 'NUMERIC%' 
		THEN @ResultQuery + '''+ COALESCE(CONVERT(VARCHAR,' + @ColumnName + '),''NULL'') +'''
		WHEN @DataType = 'TEXT' 
		THEN @ResultQuery + '''+ COALESCE('''''''' + CONVERT(VARCHAR,' + @ColumnName + ') + '''''''',''NULL'') +'''
		WHEN @DataType LIKE '%INT' 
		THEN @ResultQuery + '''+ COALESCE(CONVERT(VARCHAR,' + @ColumnName + '),''NULL'') +'''
		WHEN @DataType = 'FLOAT' 
		THEN @ResultQuery + '''+ COALESCE(CONVERT(VARCHAR,' + @ColumnName + '),''NULL'') +'''
		ELSE @ResultQuery + '''+ COALESCE(' + @ColumnName + ',''NULL'') +'''
	END
	--SET @ResultQuery = @ResultQuery + '   -- ' + @DataType	
	
	FETCH NEXT FROM TableCursor INTO @ColumnName, @DataType, @MaxLength, @precision, @scale, @is_nullable, @PrimaryKey, @ColumnID
END
CLOSE TableCursor
DEALLOCATE TableCursor

SET @SelectQuery = @SelectQuery + ') VALUES' 
IF @IndividualInserts=0 SET @SelectQuery = @SelectQuery + ''''
SET @ResultQuery = @ResultQuery + ')'' FROM ' + @TableName + ';'

SET @SQL = @SelectQuery + @ResultQuery

--SELECT @SelectQuery
--SELECT @ResultQuery

--SELECT @SQL

--PRINT @SelectQuery 
--PRINT @ResultQuery
EXEC sp_executesql @SQL

This script may not work in every single case, as there are data types not accounted for here.

 

Use “IS NULL” Rather Than “= NULL” When Upgrading to Newer Versions of SQL Server

Microsoft SQL Server logo

I’ve been upgrading an old Sybase PowerBuilder 6.5 app to a much newer version, SAP PowerBuilder 12.6.

During this little adventure, I’ve had to update much of the hard-coded SQL code that was originally written for SQL Server 4.2, and was later upgraded to SQL Server 2000.

This application’s database had been running on SQL Server 2008 since it was still compatible with SQL Server 2000 via the code below:

</p>
<p>USE master<br />GO</p>
<p>ALTER DATABASE appdatabase<br />SET COMPATIBILITY_LEVEL = 80</p>
<p>

Since we’re about to hit Microsoft’s End of Life for any version of SQL Server that will allow compatibility with SQL Server 2000, any code which is not obsolete must be rewritten.

I knew that using “*=” and “=*” instead of LEFT JOIN and RIGHT JOIN would have to change. What I had never seen before is that using “= NULL” rather than “IS NULL” in a WHERE clause would no longer work. Why that is makes sense; it’s just that older versions of SQL were more forgiving for straying from the ANSI standard.

If you have an older program and you upgrade the SQL Server database and all of a sudden you get “no rows returned” on queries that should be returning data, this could be your problem.