/*******************************************************************************
*                                                                       
*N  {xml_sample.c}  -- demonstrates how to create xml column and xml index,
*                            and how to perform xml editing and xml query
*   
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*P  Purpose:
*
*    This sample C program demonstrates how to: 
*		(1) create XML column;
*		(2) create XML tags;
*		(3) create XML index;
*		(4) edit XMLs;
*		(5) query with XPATH;
*    
*E                                                                 
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
* How to compile:: 
*	 Please refer section "Build C Samples" in the SDE sample
*
*E
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
* Usage:
*     xml_sample.c {server} {instance} {database} {user} {password} {keywords}  
*
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*X  Legalese:
*
* Copyright � 2007 ESRI
*
* All rights reserved under the copyright laws of the United States and 
* applicable international laws, treaties, and conventions.
*
* You may freely redistribute and use this sample code, with or without 
* modification, provided you include the original copyright notice and use 
* restrictions.  
*
* Disclaimer:  THE SAMPLE CODE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 
* WARRANTIES, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ESRI OR 
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
* OR BUSINESS INTERRUPTION) SUSTAINED BY YOU OR A THIRD PARTY, HOWEVER CAUSED 
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
* TORT ARISING IN ANY WAY OUT OF THE USE OF THIS SAMPLE CODE, EVEN IF ADVISED 
* OF THE POSSIBILITY OF SUCH DAMAGE.
* 
* For additional information, contact:
* Environmental Systems Research Institute, Inc.
* Attn: Contracts and Legal Services Department
* 380 New York Street
* Redlands, California, 92373
* USA
* 
* email: contracts@esri.com
*
*E
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "sdetype.h"
#include "sdeerno.h"


/* Local defines */
#define KEYCOL   "KEYCOL"
#define STRCOL   "STRCOL"
#define XMLCOL   "XMLCOL"
#define ROWIDCOL "ROWIDCOL"

#define DOC1 "<?xml version = \"1.0\" ?>\
<xmldocmetadata>\
  <Esri>\
    <gn>\
      <coverage>1000</coverage>\
      <suptheme>4</suptheme>\
      <suptheme>5</suptheme>\
      <suptheme>18</suptheme>\
      <featured>2000</featured>\
    </gn>\
  </Esri>\
</xmldocmetadata>"

#define DOC2 "<?xml version = \"1.0\" ?>\
<xmldocmetadata>\
  <Esri MetaID=\"2\">\
    <gn>\
      <coverage>5000</coverage>\
      <suptheme>400</suptheme>\
      <featured>6000</featured>\
    </gn>\
  </Esri>\
</xmldocmetadata>"

#define DOC3 "<?xml version = \"1.0\" ?>\
<xmldocmetadata>\
  <Esri MetaID=\"3\">\
    <gn>\
      <coverage >9000</coverage>\
      <suptheme>500</suptheme>\
      <suptheme>600</suptheme>\
      <featured>900</featured>\
    </gn>\
  </Esri>\
</xmldocmetadata>"

#define UPDATEDOC1 "<?xml version = \"1.0\" ?>\
<xmldocmetadata>\
  <Esri MetaID=\"1000\">\
    <gn>\
      <coverage>5500</coverage>\
      <suptheme>1500</suptheme>\
	  <featured>1500</featured>\
    </gn>\
  </Esri>\
</xmldocmetadata>"

#define UPDATEDOC2 "<?xml version = \"1.0\" ?>\
<xmldocmetadata>\
  <Esri MetaID=\"2000\">\
    <gn>\
      <coverage>1</coverage>\
      <suptheme>2</suptheme>\
      <featured>150</featured>\
    </gn>\
  </Esri>\
</xmldocmetadata>"

/* Function macros */
#define check_rc_return_on_failure(c,s,rc,f) \
	{if(rc!= SE_SUCCESS) {check_rc_(c,s,rc,f,__LINE__,__FILE__);return SE_FAILURE; }}

/* Local Function Prototypes */
void check_rc_(SE_CONNECTION Connection, SE_STREAM Stream, LONG rc, 
				 char *comment, LONG line_no, char* file_name);
static LONG S_create_table(SE_CONNECTION connection,CHAR *table,CHAR *keyword);
static LONG S_insert_rows(SE_CONNECTION  handle,const CHAR *table);
static LONG S_update_rows(SE_CONNECTION  handle,const CHAR *table,int num_of_row, LONG* keyvals);
static LONG S_query_table (SE_CONNECTION   connection, 
                          const CHAR      *table,
						  const CHAR	  *where,
                          const CHAR      *xpath);

LONG main(int argc, char *argv[]){
	CHAR   				*server, 
       	       			*user, 
						*passwd, 
						*database,
						*instance,
						*keyword;
	SE_CONNECTION 		conn;
	SE_ERROR 			error;
	LONG				result;
	CHAR				tablename[SE_MAX_TABLE_LEN+1];
	CHAR				**xml_tag_name;
    SE_XMLTAGINFO		xml_tag;
    CHAR				xml_index_name[SE_MAX_XML_INDEX_NAME_LEN+1];
    SE_XMLINDEXINFO		xml_index;
    SE_XMLCOLUMNINFO	xmlcol;
	LONG				i,tag_types[4];

	LONG			    updaterows,update_keyvals[10];
	CHAR				where[1024];
	CHAR				xpath[128];

	if(argc<7){
		printf("Usage %s <server> <instance> <database> <user> <password> <keyword>",argv[0]);
		exit(1);
	}

	server=argv[1];
	instance=argv[2];
	database=argv[3];
	user=argv[4];
	passwd=argv[5];
	keyword=argv[6];

	strcpy(tablename,"xml_samlple");

	//connect to sde server 
	result = SE_connection_create( server, instance, database, user, passwd, &error, &conn );
	check_rc_return_on_failure (conn,NULL,result,"SE_connection_create");
	printf("Connected to %s:%s as %s\n",server,instance,user);

	//Create the sample table
	printf("\nCreating sample table......\n");
	result=S_create_table(conn,tablename,keyword);
	if(result!=SE_SUCCESS)
		return result;

	//Init a xmlindex object
    result = SE_xmlindexinfo_create (&xml_index);
    check_rc_return_on_failure (conn,NULL,result,"SE_xmlindexinfo_create");
    result = SE_xmlindexinfo_set_type (xml_index, SE_XML_INDEX_DEFINITION);
	check_rc_return_on_failure (conn,NULL,result,"SE_xmlindexinfo_set_type");
	//prepare xtags
	xml_tag_name = (CHAR **) malloc (4 * sizeof(CHAR *));
	for(i=0;i<4;i++){
		xml_tag_name[i]=(CHAR *) malloc (SE_MAX_XML_TAG_NAME_LEN * sizeof(CHAR));
		tag_types[i]=SE_XML_INDEX_DOUBLE_TYPE;
	}
    strcpy(xml_tag_name[0],"/xmldocmetadata/Esri/gn/suptheme");
    strcpy(xml_tag_name[1],"/xmldocmetadata/Esri/gn/coverage");
    strcpy(xml_tag_name[2],"/xmldocmetadata/Esri/gn/featured");
    strcpy(xml_tag_name[3],"/xmldocmetadata/Esri@MetaID");
    result = SE_xmltaginfo_create (&xml_tag);
	//Add xmltags to the xmlindex object
    check_rc_return_on_failure (conn,NULL,result,"SE_xmltaginfo_create");
    for (i = 0; i < 4; i++)
    {
      result = SE_xmltaginfo_set_name (xml_tag, xml_tag_name[i]);
      check_rc_return_on_failure (conn,NULL,result,"SE_xmltaginfo_set_name");
      result = SE_xmltaginfo_set_data_type (xml_tag,tag_types[i]);
      check_rc_return_on_failure (conn,NULL,result,"SE_xmltaginfo_set_data_type");
      result = SE_xmlindexinfo_add_tag (xml_index, xml_tag);
      check_rc_return_on_failure (conn,NULL,result,"SE_xmlindexinfo_add_tag");
    }
	free(xml_tag_name);

	//Create a xml column
	printf("\nCreating XML column XMLCOL......\n");
    result = SE_xmlcolumninfo_create(&xmlcol);
	check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumninfo_create");
    result = SE_xmlcolumninfo_set_xml_column(xmlcol, tablename, XMLCOL);
    check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumninfo_set_xml_column");
    result = SE_xmlcolumninfo_set_creation_keyword (xmlcol,keyword);
    check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumninfo_set_creation_keyword");
    result = SE_xmlcolumn_create (conn, xmlcol);
    check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumn_create");

	//Create xml index on the xml column 
	printf("\nCreate index on XMLCOL......\n");
    strcpy (xml_index_name, "xmlindex");
    result = SE_xmlindexinfo_set_name (xml_index, xml_index_name);
    check_rc_return_on_failure (conn,NULL,result,"SE_xmlindexinfo_set_name");
    result = SE_xmlcolumninfo_set_index (xmlcol, xml_index);
	check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumninfo_set_index");
    result = SE_xmlcolumn_alter (conn, xmlcol);
	check_rc_return_on_failure (conn,NULL,result,"SE_xmlcolumn_alter");

	//Insert 30 rows into the table
	printf("\nInsert 30 rows into the sample table......\n");
	result=S_insert_rows(conn,tablename);
	if(result!=SE_SUCCESS)
		return result;

	sprintf(where,"KEYCOL > 10 AND KEYCOL< 20");
	strcpy(xpath,"/xmldocmetadata/Esri/gn[featured>1000]");

	//Perform query on the table without xpath
	printf("\nQuery the sample table without xpath.....\n");
	result=S_query_table(conn,tablename,where,NULL);

	//Perform query on the table with xpath
	printf("\nQuery the sample table with xpaths.....\n");
	result=S_query_table(conn,tablename,where,xpath);
	if(result!=SE_SUCCESS)
		return result;

	//Update the first 9 rows: row 10-19
	printf("\nUpdateing rows.....\n");
	updaterows=9;
	for(i=0;i<updaterows;i++)
		update_keyvals[i]=i+10;
	result=S_update_rows(conn,tablename,updaterows,update_keyvals);
	if(result!=SE_SUCCESS)
		return result;

	//Perform query on the table without xpath again 
	printf("\nQuery the sample table without xpath.....\n");
	result=S_query_table(conn,tablename,where,NULL);
	if(result!=SE_SUCCESS)
		return result;

	//Perform query on the table with xpath again
	printf("\nQuery the sample table with xpaths.....\n");
	result=S_query_table(conn,tablename,where,xpath);
	if(result!=SE_SUCCESS)
		return result;

	//Clean up
	SE_table_delete(conn,tablename);
    SE_xmlcolumninfo_free(xmlcol);
    SE_xmlindexinfo_free(xml_index);
	SE_xmltaginfo_free(xml_tag);
	SE_connection_free(conn);

	return SE_SUCCESS;
}


/***********************************************************************
*
*N  {S_create_table}  -  create a test table with provided specification
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*P  Purpose:
*     Create a sample table with provided specification
*E
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*A  Parameters:
*     <connection>  ==  (SE_CONNECTION) The connection handle.
*     <table >      ==  (CHAR*) the table name
*     <keyword >    ==  (CHAR*) the keyword for table creation
*E
***********************************************************************/
static LONG S_create_table(SE_CONNECTION connection,CHAR *table,CHAR *keyword)
{
	SHORT               number_of_columns;
	SE_COLUMN_DEF       column_definitions[3];
	LONG                result;
	SE_REGINFO          registration;

	/* Prepare the column definitions */
	number_of_columns = 2;
	strcpy (column_definitions[0].column_name,KEYCOL);
	column_definitions[0].sde_type = SE_INT32_TYPE;
	column_definitions[0].size = 0;
	column_definitions[0].decimal_digits = 0;
	column_definitions[0].nulls_allowed = TRUE;

	strcpy(column_definitions[1].column_name,STRCOL);
	column_definitions[1].sde_type = SE_STRING_TYPE;
	column_definitions[1].size = 128;
	column_definitions[1].decimal_digits = -1;
	column_definitions[1].nulls_allowed = FALSE;

	//delete the table if already existed
	result = SE_table_delete(connection,table);
	if(result!=SE_TABLE_NOEXIST)
		check_rc_return_on_failure (connection,NULL,result,"SE_table_delete");
	//create the sample table
	result = SE_table_create (connection,table,number_of_columns,
							column_definitions,keyword);
	check_rc_return_on_failure (connection,NULL,result,"SE_table_create");

	/* Add an SDE maintained rowid to the table*/
	result = SE_reginfo_create (&registration);
	check_rc_return_on_failure (connection,NULL,result,"SE_reginfo_create");
	result = SE_registration_get_info (connection,table,registration);
	check_rc_return_on_failure (connection,NULL,result,"SE_registration_get_info");
	result = SE_reginfo_set_creation_keyword (registration,keyword);
	check_rc_return_on_failure (connection,NULL,result,"SE_reginfo_set_creation_keyword");
	check_rc_return_on_failure (connection,NULL,result,"SE_reginfo_set_description");
	result = SE_reginfo_set_rowid_column (registration,ROWIDCOL,
							   SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE);
	check_rc_return_on_failure (connection,NULL,result,"SE_reginfo_set_rowid_column");
	result = SE_registration_alter (connection,registration);
	check_rc_return_on_failure (connection,NULL,result,"SE_registration_alter");
	SE_reginfo_free(registration);

	return(SE_SUCCESS);
}           


/***********************************************************************
*
*N  {S_insert_rows}  -  insert 30 rows into the table
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*P  Purpose:
*   Insert rows into the sample table. 
*   The value of the XML column will be determice by the value of KEYCOL:
*      KEYCOL%3==0: XML=DOC1
*      KEYCOL%3==1: XML=DOC2
*      KEYCOL%3==2: XML=DOC3
*E
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*A  Parameters:
*     <handle>        ==  (SE_CONNECTION) The connection handle.
*     <table >        ==  (CHAR*) the layer name
*E
***********************************************************************/
static LONG S_insert_rows(SE_CONNECTION  handle,const CHAR *table)
{
	SE_STREAM       stream;
	LONG            result,row,i;
	SHORT           number_of_columns;
	CHAR            **base_columns;
	SE_INT32        key_val;
	CHAR			str_val[128];
	SE_XMLDOC		xmldoc;

	/*Init the column names for insertion*/
	number_of_columns = 3;
	base_columns = (CHAR **) malloc (number_of_columns * sizeof(CHAR *));
	for(i=0;i<number_of_columns;i++)
		base_columns[i]=(CHAR *) malloc (SE_MAX_COLUMN_LEN * sizeof(CHAR));
	strcpy(	base_columns[0],KEYCOL);
	strcpy(	base_columns[1],STRCOL);
	strcpy(	base_columns[2],XMLCOL);

	//Create a XMLDOC object
	result = SE_xmldoc_create (&xmldoc);
	check_rc_return_on_failure (handle,NULL,result,"SE_xmldoc_create");

	/*Init an insert stream*/
	result = SE_stream_create (handle,&stream);
	check_rc_return_on_failure (handle,NULL,result,"SE_stream_create");

	result = SE_stream_insert_table (stream,table,number_of_columns,
								   (const CHAR **)base_columns);
	check_rc_return_on_failure (NULL,stream,result,"SE_stream_insert_table");

	//Insert 30 rows into the sample table
	for (row = 0;row < 30 ;row++)
	{
		//prepare values for each column
		key_val=row; 
		sprintf(str_val,"Row %d",row+1);
		switch (key_val % 3)
		{
		  case 0:
			  result = SE_xmldoc_set_text (xmldoc, (LONG)(strlen (DOC1)+1)*sizeof(CHAR), DOC1);
			  check_rc_return_on_failure (handle, NULL, result, "SE_xmldoc_set_text");
			  break;
		  case 1:
			  result = SE_xmldoc_set_text (xmldoc, (LONG)(strlen (DOC2)+1)*sizeof(CHAR), DOC2);
			  check_rc_return_on_failure (handle, NULL, result, "SE_xmldoc_set_text");
			  break;
		  case 2:
			  result = SE_xmldoc_set_text (xmldoc, (LONG)(strlen (DOC3)+1)*sizeof(CHAR), DOC3);
			  check_rc_return_on_failure (handle, NULL, result, "SE_xmldoc_set_text");
			  break;
		}
		
		//set value for each column
		result = SE_stream_set_integer(stream,1,&key_val);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_set_integer");
		result = SE_stream_set_string(stream,2,str_val);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_set_string");
		result = SE_stream_set_xml(stream,3,xmldoc);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_set_xml");

		//Execute the insertion
		result = SE_stream_execute (stream);
		if(result!=SE_SUCCESS){
			SE_stream_free(stream);
			SE_xmldoc_free(xmldoc);
			free(base_columns);
			check_rc_return_on_failure (NULL,stream,result,"SE_stream_execute");
		}
	}
	
	//free resources
	result = SE_stream_free(stream);
	check_rc_return_on_failure (NULL,stream,result,"SE_stream_free");
	SE_xmldoc_free(xmldoc);
	for(i=0;i<number_of_columns;i++)
		free(base_columns[i]);
	free(base_columns);
	return(SE_SUCCESS);
}

/***********************************************************************
*
*N  {S_update_rows}  -  update rows into a table
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*P  Purpose:
*   Update rows into the table. 
*
*   The value of the XML column will be determice by the value of KEYCOL:
*      KEYCOL%2==0: XML=UPDATEDOC1
*      KEYCOL%2==1: XML=UPDATEDOC2
*E
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*A  Parameters:
*     <handle>        ==  (SE_CONNECTION) The connection handle.
*     <table >        ==  (CHAR*) the layer name
*     <num_of_row>    ==  (LONG) number of rows to be inserted
*     <keyvals>       ==  (LONG*) specify rows to be updated
*E
***********************************************************************/
static LONG S_update_rows(SE_CONNECTION  handle,const CHAR *table,int num_of_row, LONG* keyvals)
{
	SE_STREAM       stream;
	LONG            result,row,i;
	SHORT           number_of_columns;
	CHAR            **base_columns;
	CHAR		    str_val[128];
	SE_XMLDOC		xmldoc;
	CHAR			where_clause[256];

	//Prepare column names for the update
	number_of_columns = 2;
	base_columns = (CHAR **) malloc (number_of_columns * sizeof(CHAR *));
	for(i=0;i<number_of_columns;i++)
		base_columns[i]=(CHAR *) malloc (SE_MAX_COLUMN_LEN * sizeof(CHAR));
	strcpy(	base_columns[0],STRCOL);
	strcpy(	base_columns[1],XMLCOL);
	
	//Create a XMLDOC object
	result = SE_xmldoc_create (&xmldoc);
	check_rc_return_on_failure (handle,NULL,result,"SE_xmldoc_create");

	//Prepara an update stream
	result = SE_stream_create (handle,&stream);
	check_rc_return_on_failure (handle,NULL,result,"SE_stream_create");

	for (row = 0;row < num_of_row ;row++)
	{
		//Prepare the where clauses for the update
		sprintf(where_clause,"%s = %d",KEYCOL,keyvals[row]);

		//Prepare values for each column
		sprintf(str_val,"Updated Row %d",keyvals[row]);
		if(keyvals[row]%2==0){
			  result = SE_xmldoc_set_text (xmldoc, (LONG)(strlen (UPDATEDOC1)+1)*sizeof(CHAR), UPDATEDOC1);
			  check_rc_return_on_failure (handle, NULL, result, "SE_xmldoc_set_text");
		}else{
			  result = SE_xmldoc_set_text (xmldoc, (LONG)(strlen (UPDATEDOC2)+1)*sizeof(CHAR), UPDATEDOC2);
			  check_rc_return_on_failure (handle, NULL, result, "SE_xmldoc_set_text");
		}

		result = SE_stream_update_table(stream, (const CHAR *)table, number_of_columns,
								   (const CHAR **)base_columns,(const CHAR*)where_clause);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_update_table");

		//set value for each column
		result = SE_stream_set_string(stream,1,str_val);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_set_string");
		result = SE_stream_set_xml(stream,2,xmldoc);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_set_xml");

		//Execute the update
		result = SE_stream_execute (stream);
		if( (result!=SE_SUCCESS) && (result!=SE_NO_ROWS_UPDATED)){
			SE_stream_free(stream);
			SE_xmldoc_free(xmldoc);
			free(base_columns);
			check_rc_return_on_failure (NULL,stream,result,"SE_stream_execute");
		}

		//Close the stream.
		result = SE_stream_close (stream,FALSE /* Don't reset. */);
		check_rc_return_on_failure (NULL,stream,result,"SE_stream_close");

	}//END FOR

	//free resources
	result = SE_stream_free(stream);
	check_rc_return_on_failure (NULL,stream,result,"SE_stream_free");
	SE_xmldoc_free(xmldoc);
	for(i=0;i<number_of_columns;i++)
		free(base_columns[i]);
	free(base_columns);

	return(SE_SUCCESS);
}

/***********************************************************************
*
*N  {S_query_table}  -  query the table
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*P  Purpose:
*   Query the table with specified where clause and xpath
*
*E
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*A  Parameters:
*     <handle>        ==  (SE_CONNECTION) The connection handle.
*     <table >        ==  (CHAR*) the layer name
*     <where>         ==  (const CHAR*) the where clause
*     <xpath>         ==  (const CHAR*) the xpath
*E
***********************************************************************/
static LONG S_query_table(SE_CONNECTION   connection, 
                          const CHAR      *table,
						  const CHAR	  *where,
                          const CHAR      *xpath)
{
	SE_XMLDOC			xmldoc;
	LONG				result,keyval,i,fetched_rows=0,rowid;
	CHAR				**columns;
	SE_QUERYINFO		qinfo;
	SE_STREAM			query_stream;
	CHAR				orderby[128];
	SHORT				num_of_column;
	CHAR			    str_val[128];

	printf("\t%-11s: %s\n","WHERE",where);
	printf("\t%-11s: %s\n","XPATH",xpath==NULL?"NULL":xpath);

	//prepare columns to be queried
	num_of_column = 4;
	columns = (CHAR **) malloc (num_of_column * sizeof(CHAR *));
	for(i=0;i<num_of_column;i++)
		columns[i]=(CHAR *) malloc ((SE_MAX_COLUMN_LEN+1) * sizeof(CHAR));
	strcpy(	columns[0],ROWIDCOL);
	strcpy(	columns[1],KEYCOL);
	strcpy(	columns[2],STRCOL);
	strcpy( columns[3],XMLCOL);
	
	//Prepare a XMLDOC object
	result = SE_xmldoc_create (&xmldoc);
	check_rc_return_on_failure (connection,NULL,result,"SE_xmldoc_create");

	//Init a query stream
	result=SE_stream_create(connection, &query_stream);
	check_rc_return_on_failure (connection,NULL,result,"SE_stream_create");
	result = SE_queryinfo_create(&qinfo);
	check_rc_return_on_failure (connection,NULL,result,"SE_queryinfo_create");
	result = SE_queryinfo_set_tables (qinfo, 1, (const CHAR**)&table, NULL);
	check_rc_return_on_failure (connection,NULL,result,"SE_queryinfo_set_tables");
	result = SE_queryinfo_set_columns (qinfo, num_of_column, (const CHAR **)columns);
	check_rc_return_on_failure (connection,NULL,result,"SE_queryinfo_set_columns");
	result = SE_queryinfo_set_where_clause(qinfo,where);
	check_rc_return_on_failure (connection,NULL,result,"SE_queryinfo_set_where_clause");
	sprintf(orderby,"order by %s",KEYCOL);
	result = SE_queryinfo_set_by_clause(qinfo,orderby);
	check_rc_return_on_failure (connection,NULL,result,"SE_queryinfo_set_by_clause");

	//Apply the xpath as needed
	if (xpath){
		result = SE_queryinfo_set_xpath_constraint (qinfo, table,XMLCOL,xpath);
		check_rc_return_on_failure (NULL,query_stream,result,"SE_queryinfo_set_xpath_constraint");
	}

	//Execute the query
	result = SE_stream_query_with_info (query_stream,qinfo);
	check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_query_with_info");
    result = SE_stream_execute (query_stream);
	check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_execute");

	//fetech rows
	while(result==SE_SUCCESS){
	   result = SE_stream_fetch (query_stream);
	   if(result!=SE_FINISHED){
		    check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_fetch");
		    fetched_rows++;
			//get values for each column in current row
			result = SE_stream_get_integer(query_stream,1,&rowid);
			check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_get_integer");
			result = SE_stream_get_integer(query_stream,2,&keyval);
			check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_get_integer");
			result = SE_stream_get_string(query_stream,3,str_val);
			check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_get_string");
			result = SE_stream_get_xml(query_stream,4,xmldoc);
			check_rc_return_on_failure (NULL,query_stream,result,"SE_stream_get_xml");
	   }
	}	
	printf("\t%-11s: %d rows\n","NUM_OF_ROWS",fetched_rows);

	//free resource
	SE_xmldoc_free (xmldoc);
	SE_queryinfo_free(qinfo);
	SE_stream_free(query_stream);
	for(i=0;i<num_of_column;i++)
		free(columns[i]);
	free(columns);

	return(SE_SUCCESS);
}



/***********************************************************************
*
*N  {check_rc_}  -  check the returned code
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*P  Purpose:
*     Check returned sde error code and print the error messages.
***********************************************************************/
void check_rc_(SE_CONNECTION Connection, SE_STREAM Stream, LONG rc, 
				 char *comment, LONG line_no, char* file_name) 
{

	SE_ERROR	error;
	CHAR		error_string [SE_MAX_MESSAGE_LENGTH];
	LONG		temp_rc=SE_FAILURE;


	if ((rc != SE_SUCCESS) && (rc != SE_FINISHED)) 
	{
		error_string[0] = '\0';
		SE_error_get_string (rc, error_string);

	    printf ("%s encountered a %d error:\"%s\" \nat line %ld in file %s\n",comment, rc,error_string, line_no, file_name);

		/*Print extended error info, if any */
		if ((SE_DB_IO_ERROR == rc) | (SE_INVALID_WHERE == rc) | (SE_SSA_FUNCTION_ERROR == rc)) 
		{
			if (NULL != Stream) 
			{
				/* Assume this is a stream error */
				temp_rc = SE_stream_get_ext_error (Stream, &error);
			}else if (NULL != Connection) {
				/*Assume this is a connection error */
				temp_rc = SE_connection_get_ext_error (Connection, &error);
			}
			if (SE_SUCCESS == temp_rc)
			{
					printf("Extended error code: %d, extended error string:\n%s\n",
					error.ext_error, error.err_msg1);
			}
		} /*End SE_DB_IO_ERROR */

	} 
}