Showing posts with label Oracle. Show all posts
Showing posts with label Oracle. Show all posts

Oracle NLS_LANG for language/territory/character set

Recently I had an Oracle problem at work.
I kept postponing to look with care at this problem till today.

Problem
All accented characters as à, á, é, í, ó, ú, â, ê, î, ô, û, ã, õ, etc and other ones as ç (cedilla), º, ª (ordinal indicators), were being replaced by erroneous characters as this example in the header of a stored procedure:

=================================================================
-- Revis¿o : 'N¡ã 0
-- Descri¿¿o: // description here
-- Data   : 03/29/2010
-- Autor  : CHEMTECH
=================================================================

This problem gets worse if you have something as

SELECT CONTRACT.NUM_CONTRACT,
           'N¡ã' || TO_CHAR(CONTRACT.NUM_CONTRACT) || ' - ' ||

The result of the above SQL query is used in the UI, that is, the user will see the wrong characters ¡ã instead of º. If you commit this thing in the repository you mess everything up. You see what I mean…

Questioning
At first I thought the problem was the database encoding settings.

I and only I was getting crazy characters. My development group has more 3 people and no one was having this problem. This clearly should be a UI configuration, that is, how the data is shown on my machine.

A little bit of research on the net took me to Oracle’s NLS_LANG FAQ.

This is what NLS_LANG stands for/does:

A locale is a set of information addressing linguistic and cultural requirements that corresponds to a given language and country. Traditionally, the data associated with a locale provides support for formatting and parsing of dates, times, numbers, and currencies, etc. Providing current and correct locale data has historically been the responsibility of each platform owner or vendor, leading to inconsistencies and errors in locale data.

Setting the NLS_LANG environment parameter is the simplest way to specify locale behavior for Oracle software. It sets the language and territory used by the client application and the database server. It also indicates the client's character set, which corresponds to the character set for data to be entered or displayed by a client program.

I started reading the FAQ (top bottom approach) and tried the following query:

SELECT * FROM NLS_SESSION_PARAMETERS;

which gave me this result:

NLS_SESSION_PARAMETERS on my development machine
Figure 1 - NLS_SESSION_PARAMETERS on my development machine

As you see NLS_LANGUAGE and NLS_TERRITORY are set to AMERICAN and AMERICA respectively on my machine.

I asked Thiago Arakaki a coworker of mine to execute the same SQL query on his dev box. To my surprise and delight this was the screen he got:

NLS_SESSION_PARAMETERS on Thiago's development machine
Figure 2 - NLS_SESSION_PARAMETERS on Thiago Arakaki’s development machine 

As you see NLS_LANGUAGE and NLS_TERRITORY are set to BRAZILIAN PORTUGUESE and BRAZIL respectively on his machine.

This clearly pointed what could be the cause of the problem I was having.

I couldn’t understand why my machine had a different NLS_LANGUAGE and NLS_TERRITORY.

I read the FAQ a little bit more and got to this part Where to set the NLS_LANG in Windows.

I checked Windows registry to make sure the NLS_LANG subkey was set correctly on Oracle home. I got this:

NLS_LANG subkey on Windows RegistryFigure 3 - NLS_LANG subkey on Windows Registry

For my surprise it was correct. Look at NLS_LANG. It has BRAZILIAN PORTUGUESE_BRAZIL.WE8MSWIN1252 just like Thiago Arakaki’s dev box.

So why do I still was getting wrong characters inside Allround Automations PL/SQL Developer when working on the same database that Thiago also uses? Aha, that’s the question this post tries to clarify if you come to get in this same situation.

Reading the FAQ a little bit more… :o) I saw that you could also set NLS_LANG as a System or User Environment Variable, in System properties. So I went there to check if I already had such a thingy set on my machine. Again for my surprise this thingy was there. I don’t remember if/when I created this variable.

The only thing I thought at the moment was the interference that some previous installations of Oracle could’ve caused on my machine. Today I’m using Oracle express but in the past I installed the full Oracle server. I can’t state for sure what created the NLS_LANG var on my Windows System variables.

The screen I got was something like this:

 NLS_LANG variable on Windows System variables
Figure 4 - NLS_LANG variable on Windows System variables

I had AMERICAN set for the NLS_LANG variable.

Solution
I then changed the system variable NLS_LANG to BRAZILIAN PORTUGUESE_BRAZIL.WE8MSWIN1252. Clicked OK. Closed PL/SQL. Opened PL/SQL and issued the SQL query described on the beginning of this post and could see that the NLS_LANGUAGE and NLS_TERRITORY rows had changed appropriately to BRAZILIAN PORTUGUESE and BRAZIL respectively. After that I started to get the right characters throughout the database.

Great! Problem solved.

Upload/Import Download/Export a File to/from Oracle DB

What motivated me to write this post is that this is a really interesting task.

The general idea one have about databases is that they only serve the purpose of storing plain values as numbers, dates and text. A database does not do just that but even more allowing you to store a whole file as an Excel spreadsheet or an MP3 file in a column you define in your table.

To illustrate that, let’s suppose you were assigned the task of saving a spreadsheet in the database. This spreadsheet contains valuable data (as taxes values for each federal state) that’ll be used throughout your application. A user will upload that spreadsheet to the database and when someone else want to see the content of that spreadsheet it’s just a matter of clicking a download button to get it back. Pretty simple, isn’t it?

To accomplish that I’ll use Oracle database, more specifically Oracle Database 10g Express Edition (free to use).

The SQL code I used to create the database objects is as follows:

----------------------------------------------------------
-- Export file for user ORACLETEST                      --
-- Created by Leniel Macaferi on 3/13/2010, 11:43:17 PM --
----------------------------------------------------------

-- Create database tablespace
create tablespace oracletest datafile 'C:\oracletest.dbf'
size                                  20M
autoextend on maxsize                 unlimited
extent management local uniform size  64K;

-- Create user and Grant permissions
create user oracletest identified by oracletest default tablespace oracletest;
grant connect, resource to oracletest;
grant create session to oracletest;
grant unlimited tablespace to oracletest;
grant dba to oracletest;

-- Create table
create table FILES
(
  ID          NUMBER not null,
  UPLOAD_DATE DATE not null,
  CONTENT     BLOB not null,
  TYPE        VARCHAR2(64) not null,
  NAME        VARCHAR2(128) not null,
LENGTH NUMBER not null
);
alter table oracletest.FILES
  add constraint ID primary key (ID);

-- Create sequence
create sequence oracletest.SEQ_FILES
minvalue 1
maxvalue 999999999999999999999999999
start with 1
increment by 1
nocache;
-- Create import/upload procedure
create or replace procedure sp_import_file(
upload_date
in varchar2, content files.content%type, type in varchar2, name in varchar2,
length in number, id
out number) is begin select seq_files.nextval into id from dual; insert into files (id, upload_date, content, type, name, length) values (id, to_date(upload_date, 'mm/dd/yyyy hh:mi:ss pm'), content, type, name,
length
); end sp_import_file;

I’ll use an ASP.NET MVC application to show the process of uploading/importing downloading/exporting a file to/from an Oracle database.

The app has a tab called Import/Export File. Clicking on this tab takes you to a View that contains a file upload control and two buttons. When the user clicks the Import button the file will be uploaded/imported/saved to/in the database. When the user clicks the Export button the last file saved in the database will be downloaded/exported to the user’s computer.

The following is a screenshot of the application:

Oracle Import Export File ASP.NET MVC Application
Figure 1 - ASP.NET MVC Application UI

The following is a screenshot of the Files table which already has some files:

Oracle Import Export Files Table
Figure 2 - Oracle Files Table

As you can see the thing that makes it possible to import a whole file to a table is the type of the column that’ll hold the file; in this case the type is BLOB.

In the lines that follow I’ll present the source code I used to implement this sample ASP.NET MVC application.

It’s always good to view the Solution Explorer inside Visual Studio so that I can highlight the files I have created:

Oracle Import Export File Visual Studio Solution Explorer Figure 3 - Solution Explorer in Visual Studio

This is the C# code used in the upload/import process (inside OracleFileModel.cs):

/// <summary>
/// Imports any file to an Oracle database table that has a column of type BLOB.
/// </summary>
/// <param name="uploadDate">Date of upload/importation</param>
/// <param name="fileName">File name</param>
/// <param name="filePath">File path on the server</param>
/// <param name="fileType">File type</param>
/// <param name="fileSize">File size in bytes</param> /// <returns>Id of Row just inserted</returns> public static int Import(string uploadDate, string fileName, string filePath, string fileType, int fileSize) { OracleConnection connection = GetOracleConnection(); connection.Open(); OracleTransaction transaction; transaction = connection.BeginTransaction(); OracleCommand cmd; cmd = connection.CreateCommand(); cmd.Transaction = transaction; // Creates a temporary blob object on the database. This object will store the file content. cmd.CommandText = "declare xx blob; begin dbms_lob.createtemporary(xx, false, 0); :tempblob := xx; end;"; cmd.Parameters.Add(new OracleParameter("tempblob", OracleType.Blob)).Direction = ParameterDirection.Output; cmd.ExecuteNonQuery(); // Getting the content of the file... byte[] buffer = GetFileContent(filePath); // Oracle object responsible for storing the File content. OracleLob tempLob; // Assigning tempLob the blob object created on the database. tempLob = (OracleLob)cmd.Parameters[0].Value; tempLob.BeginBatch(OracleLobOpenMode.ReadWrite); // Writing the file content to tempLob. tempLob.Write(buffer, 0, buffer.Length); tempLob.EndBatch(); cmd.Parameters.Clear(); // The name of the Procedure responsible for inserting the data in the table. cmd.CommandText = "SP_IMPORT_FILE"; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new OracleParameter("upload_date", OracleType.VarChar)).Value = uploadDate; cmd.Parameters.Add(new OracleParameter("content", OracleType.Blob)).Value = tempLob; cmd.Parameters.Add(new OracleParameter("type", OracleType.VarChar)).Value = fileType; cmd.Parameters.Add(new OracleParameter("name", OracleType.VarChar)).Value = fileName;
cmd.Parameters.Add(new OracleParameter("length", OracleType.Number)).Value = fileSize; cmd.Parameters.Add(new OracleParameter("id", OracleType.Number)).Direction = ParameterDirection.Output; try { cmd.ExecuteNonQuery(); } catch (Exception ex) { transaction.Rollback(); throw new Exception(ex.Message); } transaction.Commit(); connection.Close(); // Returning the Id of the row just inserted in table FILES. // This Id could be used to associate the file inserted with another table, for example, // if you had to parse the content of a spreadsheet and save each line in other table. return int.Parse(cmd.Parameters[4].Value.ToString()); }
This is the C# code used in the download/export process (inside OracleFileModel.cs)::
/// <summary>
/// Exports the last file imported to an Oracle database table that has a column of type BLOB.
/// </summary>
/// <returns>File</returns>
public static MyFileModel Export()
{
    MyFileModel file = new MyFileModel();

    OracleConnection connection = GetOracleConnection();
    connection.Open();

    OracleCommand cmd;
    cmd = connection.CreateCommand();

    // Gets the last File imported.
    cmd.CommandText = "select content, name, type from (select content, name, type from files f order by f.upload_date desc) where rownum = 1";

    byte[] fileContent;

    try
    {
        OracleDataReader reader = cmd.ExecuteReader();

        using (reader)
        {
            reader.Read();

            if (!reader.HasRows)
            {
                return file;
            }

            OracleLob blob = reader.GetOracleLob(0);
            OracleString fileName = reader.GetOracleString(1);
            OracleString fileType = reader.GetOracleString(2);

            fileContent = new byte[blob.Length];

            blob.Read(fileContent, 0, (int)blob.Length);

            file.Content = fileContent;
            file.Name = fileName.Value;
            file.Type = fileType.Value;
        }
    }
    catch (Exception ex)
    {
        throw new Exception(ex.Message);
    }

    connection.Close();

    return file;
}
/// <summary>
/// Gets an Oracle connection to the database.
/// </summary>
/// <returns>OracleConnection</returns>
private static OracleConnection GetOracleConnection()
{
    string user = ConfigurationManager.AppSettings["user"];
    string password = ConfigurationManager.AppSettings["password"];
    string dataSource = ConfigurationManager.AppSettings["dataSource"];

   return new OracleConnection("Data Source=" + dataSource + ";User Id=" + user + ";Password=" + password + ";");
}

/// <summary>
/// Get the content of the file in byte array buffer.
/// </summary>
/// <param name="path">File path on the server</param>
/// <returns>File content</returns>
private static byte[] GetFileContent(string path)
{
    Stream fs = File.OpenRead(path);
    
    byte[] buffer = new byte[fs.Length];

    int q = fs.Read(buffer, 0, Convert.ToInt32(fs.Length));

    fs.Close();

    return buffer;
}

This is the code implemented for the OracleFileController:

public class OracleFileController : Controller
{
    //
    // GET: /OracleFile/

    public ActionResult OracleFile(FormCollection formCollection)
    {
// Handles the button that posted to the server...
// OracleFile.aspx has 2 submit buttons.


if (formCollection["Import"] != null) { Import(); } else if (formCollection["Export"] != null) { return Export(); } return View(); } /// <summary> /// Imports a file from the user computer to the database. /// </summary> /// <returns></returns> private void Import() { foreach (string fileName in Request.Files) {
// Gets the file the user selected. HttpPostedFileBase file = Request.Files[fileName];
if (file.ContentLength > 0) {
// Getting the name of the folder in which the file will be saved on the server. string saveFolder = ConfigurationManager.AppSettings["saveFolder"];
                // Creating the path for the file on the server.

string filePath = Path.Combine(Request.ServerVariables["APPL_PHYSICAL_PATH"] + saveFolder, Path.GetFileName(file.FileName));
                // Saving a copy of the user's posted file on the server.
file.SaveAs(filePath);
                // Save the file in the database.
                // Although id isn't being used, this shows you how to get the id of the file just inserted in the database.
                int id = OracleFileModel.Import(DateTime.Now.ToString(), file.FileName, filePath, file.ContentType, file.ContentLength);
                // Deleting the file just imported so that the server disk does not get full.
                System.IO.File.Delete(filePath);

                TempData["message"] = "File imported with sucess.";
            }
        }
    }

    /// <summary>
    /// Exports a file from the database to the user's computer.
    /// </summary>
    /// <returns></returns>
    public ActionResult Export()
    {
        // Get the file from the database.
        MyFileModel file = OracleFileModel.Export();

        if (file.Content != null)
        {
            TempData["message"] = "File exported with success.";

            // Return the file to the user's computer.
            return File(file.Content, file.Type, file.Name);
        }
        else
        {
            TempData["message"] = "There's nothing to download.";

            return View("OracleFile");
        }
    }
}

This is a really big post full of code. I think the code has sufficient comments that should shed some light on how to accomplish the task.

Important note
All properties read in the form of ConfigurationManager.AppSettings["property"]; including the database name and password are stored in the Web.config file under the appSettings section.

<appSettings>
   <add key="user" value="oracletest"/>
   <add key="password" value="oracletest"/>
   <add key="dataSource" value="localhost"/>
   <add key="saveFolder" value="Files"/>
</appSettings>

Hope you make good use of it.

Visual Studio 2010 C# ASP.NET MVC Application
You can get the Microsoft Visual Studio Project at:

http://sites.google.com/site/leniel/blog/OracleImportExportFile.zip

To try out the code you can use the free Microsoft Visual Studio 2010 Professional Release Candidate (ISO file) that you can get at: http://www.microsoft.com/downloads/details.aspx?FamilyID=f9c0b89b-4964-4906-94c6-60ad8a429690&displaylang=en

Although I’ve used the 2010 version of Visual Studio I think the code presented in this post works just fine with previous versions of Visual Studio and ASP.NET MVC framework. You shouldn’t have problems while copying/pasting the code in previous versions. Maybe you’ll have to change DLL references or something like that.