Retrieve the Columns and Tables of a Database in Postgres - ajax

I am working on a Spring MVC project where I need to get the tables and the attributes of a selected Postgres Database and store them in an array as soon as the page loads.
I have currently written the following ajax call through which am able to retrieve the tables and display under the respective database.
VisualEditor.js
drop: function (e, ui) {
var mouseTop = e.clientY;
var mouseLeft = e.clientX;
var dropElem = ui.draggable.attr('class');
droppedElement = ui.helper.clone();
ui.helper.remove();
$(droppedElement).removeAttr("class");
$(droppedElement).draggable({containment: "container"});
jsPlumb.repaint(ui.helper);
//If the dropped Element is a TABLE then->
if (dropElem == "stream ui-draggable ui-draggable-handle") {
var newAgent = $('<div>');
jsPlumb.addEndpoint(newAgent,connectorProperties);
newAgent.attr('id', i).addClass('streamdrop');
var elemType = "table";
$("#container").addClass("disabledbutton");
$("#toolbox").addClass("disabledbutton");
$('#container').append(newAgent);
$.ajax({
type: "post",
url: "http://localhost:8080/controllers/tables",
cache: false,
success: function(response){
if (response !== "{}"){
var array = response.replace("{", "").replace("}", "");
var index = array.indexOf("[") +1;
var stringval = array.split(":");
var stringval2 = array.substring(index,array.indexOf("]")).split(",");
var db_names = new Array();
var table_names = new Array();
var column_names = new Array();
for(var i = 0 ;i < stringval.length-1 ;i++)
{
db_names[i] = eval(stringval[i]);
if(db_names[i]=="testdb"){
var StreamArray = new Array();
}
var listId = "db"+i;
var dropdownId ="mydrpdown"+i;
table_names[i] = new Array();
$("#lot").append(
"<li onclick='myFunction(\""+dropdownId+"\","+i+","+stringval2.length+")' class='list-group-item dropbtn' id='"+listId+"'> " + db_names[i] +
"</li> "+
"<div id='" + dropdownId +"' class='dropdown-content'> " +
"<a onclick='setDatabase(\""+db_names[i]+"\",\""+listId+"\")'> Make This as Default Database</a>"+
"</div>"
);
$("#databaseID").append(
"<option>" + db_names[i] +"</option>"
);
for(var j=0;j < stringval2.length;j++)
{
/**
* Loading the Predefined Databases and Tables of the Connected DB
*/
var table_id= "tableId"+i+j;
if( eval(stringval2[j]) != null){
table_names[i][j] = eval(stringval2[j]);
if(db_names[i]=="testdb")
{
StreamArray[j] = new Array(4);
StreamArray[j][0] = table_names[i][j];
/**
* table_names array values at the moment are:
* customer,department and students
* So the following ajax call should pass each table name to the listTables
* method in the controller and fetch the respective columns of each table
*/
$.ajax({
type: "post",
url: "http://localhost:8080/controllers/listTables/{tablename}",
data: { tablename: table_names[i][j]} ,
cache: false,
success: function(response){
if (response !== "{}"){
var array = response.replace("{", "").replace("}", "");
var index = array.indexOf("[") +1;
var stringval = array.split(":");
var stringval2 = array.substring(index,array.indexOf("]")).split(",");
var db_names = new Array();
var table_names = new Array();
var column_names = new Array();
for(var i = 0 ;i < stringval.length-1 ;i++){
}
}
}
});
}
$("#lot").append(
"<li class='list-group-item'style = 'display:none' id='"+table_id+"'> "+table_names[i][j]+" </li>");
}
}
$("#lot").append(
"</br>");
array = array.slice(array.indexOf("]") +2);
index = array.indexOf("[") +1;
stringval2 = array.substring(index,array.indexOf("]")).split(",");
}
}
},
error: function(xhr, status, error){
alert("Error while loading the query");
}
});
$("property").show();
$(".toolbox-titlex").show();
$(".panel").show();
I have written 2 ajax calls embedded within to get the table_names in the first go and the column_names following that. But am guessing that this is not a very efficient way. But I am not sure how to get both at the same time.
EditorController.java
#RequestMapping(value= "/tables", method = RequestMethod.POST)
public #ResponseBody
String tables(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Map<String, List<String>> alvalues = new HashMap<String, List<String>>();;
String queryString = request.getParameter("query");
// ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Module.xml");
//CustomerDao customerDAO = (CustomerDao) context.getBean("CustomerDao");
alvalues = customerDAO.getAllTables();
Gson gson = new Gson();
String element = gson.toJson(alvalues);
System.out.println(element);
return element;
}
#RequestMapping(value= "/listTables/{tablename}", method = RequestMethod.POST)
public #ResponseBody
String listTables(HttpServletRequest request, HttpServletResponse response,#PathVariable("tablename") String tablename)
throws Exception {
Map<String, List<String>> alvalues = new HashMap<String, List<String>>();;
String queryString = request.getParameter("query");
alvalues = customerDAO.getAllFields(tablename);
Gson gson = new Gson();
String element = gson.toJson(alvalues);
System.out.println(element);
return element;
}
CustomerDAO.java
public Map<String, List<String>> getAllTables();
public Map<String, List<String>> getAllFields(String tablename);
jdbcCustomerDAO.java
public Map<String, List<String>> getAllTables(){
int k = 1;
Map<String, List<String>> map = new HashMap<String, List<String>>();
ArrayList<String> databases = getAllDatabse();
String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema='public'";
Connection conn = null;
for(int i=0;i<=databases.size();i++)
{
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/"+databases.get(i),"postgres", "123");
PreparedStatement ps = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = ps.executeQuery();
ArrayList<String> alvalues = new ArrayList<String>();
while(rs.next()){
alvalues.add(rs.getString(1));
}
map.put(databases.get(i), alvalues);
rs.beforeFirst();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Key = " + key);
System.out.println("Values = " + values + "n");
}
conn.close();
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
return map;
}
public Map<String, List<String>> getAllFields(String tablename){
int k = 1;
Map<String, List<String>> map = new HashMap<String, List<String>>();
ArrayList<String> databases = getAllDatabse();
String sql = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '"+tablename+"'";
Connection conn = null;
for(int i=0;i<=databases.size();i++)
{
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/"+databases.get(i),"postgres", "123");
PreparedStatement ps = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = ps.executeQuery();
ArrayList<String> alvalues = new ArrayList<String>();
while(rs.next()){
alvalues.add(rs.getString(1));
}
map.put(databases.get(i), alvalues);
rs.beforeFirst();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Key = " + key);
System.out.println("Values = " + values + "n");
}
conn.close();
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
return map;
}
But am not able to get the column names successfully.
Console Log
According to this, the table_names are successfully retrieved but the column names are empty.
INFO : com.postgres.controllers.HomeController - Welcome home! The client locale is en_US.
postgres
postgreside
testdb
Key = postgres
Values = []n
Key = postgreside
Values = [constraints, users, notes, session, projects, databases, tables, columns, index, checksconstraint, additionalproperties, foreignref, primaryref, referenceproperties, department, person]n
Key = postgres
Values = []n
Key = testdb
Values = [customer, department, students]n
Key = postgreside
Values = [constraints, users, notes, session, projects, databases, tables, columns, index, checksconstraint, additionalproperties, foreignref, primaryref, referenceproperties, department, person]n
Key = postgres
Values = []n
Index: 3, Size: 3
{"testdb":["customer","department","students"],"postgreside":["constraints","users","notes","session","projects","databases","tables","columns","index","checksconstraint","additionalproperties","foreignref","primaryref","referenceproperties","department","person"],"postgres":[]}
postgres
postgreside
testdb
postgres
postgreside
testdb
postgres
postgreside
testdb
Key = postgres
Values = []n
Key = postgres
Values = []n
Key = postgres
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Key = testdb
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Index: 3, Size: 3
{"testdb":[],"postgreside":[],"postgres":[]}
Key = testdb
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Index: 3, Size: 3
{"testdb":[],"postgreside":[],"postgres":[]}
Key = testdb
Values = []n
Key = postgreside
Values = []n
Key = postgres
Values = []n
Index: 3, Size: 3
{"testdb":[],"postgreside":[],"postgres":[]}
I would like to have a single method to retrieve the table names and their attributes/columns and store it in the StreamArray so that I don't need to access the DB everytime I need to refer to the related data.
Any suggestions on how I could get the column names and store it in the StreamArray that I've created under the VisualEditor.js will be highly appreciated.

In the general case, you can have more than one database to deal with, more than one schema, and each table name can appear in more than one schema. In your case, there might be more schemas you need to exclude. Or you might decide that only the schema 'public' is relevant. (Be careful with decisions like that.)
select table_catalog, table_schema, table_name, column_name
from information_schema.columns
where table_catalog = 'your_db_name'
and table_schema <> 'information_schema'
and table_schema <> 'pg_catalog'
order by table_catalog, table_schema, table_name, column_name;

Related

oracle jdbc driver reports no primary key columns on a table that has a primary key

This was reported by HibernateTools Reverse Engineering, but it seems to be true.
oracle jdbc driver reports no primary key columns on a table that has a primary key.
#Test
public void checkTable() throws SQLException, IOException {
System.out.println("in check table");
assertNotNull(conn);
Statement s = conn.createStatement();
ResultSet rset = s.executeQuery("select user from dual");
rset.next();
String username = rset.getString(1);
rset.close();
try {
s.execute("drop table " + username + ".x");
} catch (Exception e) {
// nothing it might not exist
}
s.execute("create table " + username + ".x (y number)");
s.execute("alter table x add constraint x_pk primary key (y)");
DatabaseMetaData meta = conn.getMetaData();
final String[] tableTypes = new String[] { "TABLE", "VIEW" };
ResultSet rs = meta.getTables(null, username, "X",tableTypes);
rs.next();
String table = rs.getString("table_name");
System.out.println("table is " + table);
rs.close();
rs = s.executeQuery("select * from user_constraints where table_name = 'X'");
rs.next();
String type = rs.getString("constraint_type");
assertEquals("P",type); // primary key
rs.close();
rs = meta.getPrimaryKeys(null, username, "X");
rs.next();
logger.info("getting pk");
System.out.print("wtf");
int colCount = 0;
while (rs.next()) {
final String pkName = rs.getString("pk_name");
logger.info("pkName: {}", pkName);
int keySeq = rs.getShort("key_seq"); // TODO should probably be column seq
String columnName = rs.getString("column_name");
logger.warn("seq: {}, columnName: {}, keySeq, columnName");
colCount++;
}
System.out.println("colCount: " + colCount);
assertEquals(1,colCount);
}

Hextoraw() not working with IN clause while using NamedParameterJdbcTemplate

I am trying to update certain rows in my oracle DB using id which is of RAW(255).
Sample ids 0BF3957A016E4EBCB68809E6C2EA8B80, 1199B9F29F0A46F486C052669854C2F8...
#Autowired
private NamedParameterJdbcTemplate jdbcTempalte;
private static final String UPDATE_SUB_STATUS = "update SUBSCRIPTIONS set status = :status, modified_date = systimestamp where id in (:ids)";
public void saveSubscriptionsStatus(List<String> ids, String status) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();
List<String> idsHexToRaw = new ArrayList<>();
String temp = new String();
for (String id : ids) {
temp = "hextoraw('" + id + "')";
idsHexToRaw.add(temp);
}
paramSource.addValue("ids", idsHexToRaw);
paramSource.addValue("status", status);
jdbcTempalte.update(*UPDATE_SUB_STATUS*, paramSource);
}
This above block of code is executing without any error but the updates are not reflected to the db, while if I skip using hextoraw() and just pass the list of ids it works fine and also updates the data in table. see below code
public void saveSubscriptionsStatus(List<String> ids, String status) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();]
paramSource.addValue("ids", ids);
paramSource.addValue("status", status);
jdbcTempalte.update(UPDATE_SUB_STATUS, paramSource);
}
this code works fine and updates the table, but since i am not using hextoraw() it scans the full table for updation which I don't want since i have created indexes. So using hextoraw() will use index for scanning the table but it is not updating the values which is kind of weird.
Got a solution myself by trying all the different combinations :
#Autowired
private NamedParameterJdbcTemplate jdbcTempalte;
public void saveSubscriptionsStatus(List<String> ids, String status) {
String UPDATE_SUB_STATUS = "update SUBSCRIPTIONS set status = :status, modified_date = systimestamp where id in (";
MapSqlParameterSource paramSource = new MapSqlParameterSource();
String subQuery = "";
for (int i = 0; i < ids.size(); i++) {
String temp = "id" + i;
paramSource.addValue(temp, ids.get(i));
subQuery = subQuery + "hextoraw(:" + temp + "), ";
}
subQuery = subQuery.substring(0, subQuery.length() - 2);
UPDATE_SUB_STATUS = UPDATE_SUB_STATUS + subQuery + ")";
paramSource.addValue("status", status);
jdbcTempalte.update(UPDATE_SUB_STATUS, paramSource);
}
What this do is create a query with all the ids to hextoraw as id0, id1, id2...... and also added this values in the MapSqlParameterSource instance and then this worked fine and it also used the index for updating my table.
After running my new function the query look like : update
SUBSCRIPTIONS set status = :status, modified_date = systimestamp
where id in (hextoraw(:id0), hextoraw(:id1), hextoraw(:id2)...)
MapSqlParameterSource instance looks like : {("id0", "randomUUID"),
("id1", "randomUUID"), ("id2", "randomUUID").....}
Instead of doing string manipulation, Convert the list to List of ByteArray
List<byte[]> productGuidByteList = stringList.stream().map(item -> GuidHelper.asBytes(item)).collect(Collectors.toList());
parameters.addValue("productGuidSearch", productGuidByteList);
public static byte[] asBytes(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}

syntax to return a number of results per page

I have this linq statement that returns result and i wanted to add the specifications of how many items are displayed per page. I know the default is 10 per page how can i change it to 40?
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
});
Get User
public virtual IEnumerable<T> GetUser<T>(int userId, Func<IDataRecord, T> modelBinder, int resultsPerPage = 10, int pageNumber = 1)
{
if (userId < 1)
throw new NullReferenceException("The sectionId cannot be null, when retreiving an element");
if (resultsPerPage < 1)
resultsPerPage = 1; // enforce bare minimum result set
if (pageNumber < 1)
pageNumber = 1; // enforce one-based page numbering
SqlCommand _command = new SqlCommand("dbo.GetUser");
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add(new SqlParameter { ParameterName = "userId", SqlDbType = SqlDbType.Int, Value = userId });
_command.Parameters.Add(new SqlParameter { ParameterName = "resultsPerPage", SqlDbType = SqlDbType.Int, Value = resultsPerPage });
_command.Parameters.Add(new SqlParameter { ParameterName = "pageNumber", SqlDbType = SqlDbType.Int, Value = pageNumber });
return DbInstance.ExecuteAs<T>(_command, modelBinder);
}
Neither Linq nor entity framework have any default number of records 'per page'. But since your GetUser function includes a resultsPerPage parameter, you can just do this:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}, 40);
To limit the number of results in Linq use the the Enumerable.Take method:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}).Take(40);

Comparing very large List<string> with database table via LINQ to Entities

I have a very large list of strings containing GUIDs and a 100,000+ record table in a database with an Entity Framework model in my app.
What' is the most efficient approach to finding all records in a particular dataset where the GUID List is not present?
The following is performing very slowly:
var list= new List<string> { "1", "2", "3" };
return (from t1 in db.Items
where (!list.Contains(t1.GUID))
When I have a lot of parameters (several hundreds or more) to a query I use bulk insert to temporary table and then join it from main table.
My code looks something like this:
private static DataTable FillDataTable(IEnumerable<int> keys) {
var dataTable = new DataTable("Stage");
dataTable.Locale = CultureInfo.CurrentCulture;
dataTable.Columns.Add("Key", typeof(int));
foreach (var key in keys) {
var row = dataTable.NewRow();
row[0] = key;
dataTable.Rows.Add(row);
}
return dataTable;
}
private static void CreateStageTable(SqlConnection connection, string tableName, DataTable dataTable) {
var sql = new StringBuilder();
sql.AppendLine("CREATE TABLE {StageTableName} ( ");
sql.AppendLine(" Key INT NOT NULL ");
sql.AppendLine(") ");
sql.Replace("{StageTableName}", SqlUtilities.QuoteName(tableName));
using (var command = connection.CreateCommand()) {
command.CommandText = sql.ToString();
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
}
using (var bulkcopy = new SqlBulkCopy(connection)) {
bulkcopy.DestinationTableName = tableName;
bulkcopy.WriteToServer(dataTable);
}
}
public void DoQuery(IEnumerable<int> keys) {
var dataTable = FillDataTable(keys);
using (var connection = new SqlConnection(_connectionString)) {
connection.Open();
CreateStageTable(connection, "#Stage", dataTable);
string sql = "SELECT x " +
"FROM tbl " +
" LEFT JOIN {StageTableName} AS Stage " +
" ON x.Key = Stage.Key "
"WHERE Stage.Key IS NULL";
...
}
}
Don't use a List, use a HashSet<string>, this will give you O(1) lookup ups instead of O(n).

Dynamic Linq to Datatable Derived Field

Is it possible to use Dynamic Linq to run a query similar to:
Select a, b, a + b as c
from MyDataTable
I have an application where the user can enter SQL statements, the results of these statements are then assigned to a DataTable. There is also the option to derive a field based on other fields. (e.g. user can say field C = a + b, or field D = A*B+10 etc).
Ideally I would like to do something similar to:
string myCalc = "Convert.ToDouble(r.ItemArray[14])+Convert.ToDouble(r.ItemArray[45])";
var parameters = from r in dt.AsEnumerable()
select (myCalc);
What I want to do in this example is add the value of column 14 to column 45 and return it. It's up to the user to decide what expression to use so the text in the select needs to be from a string, I cannot hard code the expression. The string myCalc is purely for demonstration purposes.
You could do that using a Dictionary, and a DataReader and Dynamic Queries. Here is an example based in part in Rob Connery's Massive ORM RecordToExpando:
void Main()
{
string connString = "your connection string";
System.Data.SqlClient.SqlConnection conn = new SqlConnection(connString);
string statement = "SUM = EstimatedEffort + OriginalEstimate, Original = OriginalEstimate";
// Note: You should parse the statement so it doesn't have any updates or inserts in it.
string sql = "SELECT " + statement +" FROM Activities";
List<IDictionary<string, object>> results = new List<IDictionary<string, object>>();
conn.Open();
using(conn)
{
var cmd = new SqlCommand(sql, conn);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var dic = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
dic.Add(
reader.GetName(i),
DBNull.Value.Equals(reader[i]) ? null : reader[i]);
}
results.Add(dic);
}
}
foreach (var dicRow in results)
{
foreach (string key in dicRow.Keys)
{
Console.Write("Key: " + key + " Value: " + dicRow[key]);
}
Console.WriteLine();
}
}
Something like this:
void Main()
{
var dataTable = new DataTable();
dataTable.Columns.Add("a", typeof(double));
dataTable.Columns.Add("b", typeof(double));
dataTable.Rows.Add(new object[] { 10, 20 });
dataTable.Rows.Add(new object[] { 30, 40 });
string myCalc = "Convert.ToDouble(ItemArray[0]) + Convert.ToDouble(ItemArray[1])";
var query = dataTable.AsEnumerable().AsQueryable();
var result = query.Select(myCalc);
foreach (Double c in result)
{
System.Console.WriteLine(c);
}
}

Resources