How to get column_name and table_name from DML statement? - javascript

I am building an algorithm to mock DB operations.
I have one problem, how i can get Table name and column names from a DML statement?
i.e:
string = "SELECT id,name FROM USER_TBL"
string TABLE_NAME = getTableName(string); //this will return "USER_TBL"
array COLUMN_NAME = getColumnNames(string); //this will return ["id","name"]
If i consider these as string manipulation, how i can design algorithm to get table name & column names?
Currently i use following logic,
function getTableName(iString){
//find string between "FROM " to next " "(space) and return it
}
function getColumnNames(string){
//get string between "SELECT " and " FROM" and split string based on "," (comma) and return it
}
I wanted to know is there any algorithm already available for this(for reference)? What and all the cases i need to handle other than these?

I don't know an algorithm, but a few things that come to my mind:
Table name
- Tablenames can have aliases, but with your logic that should be OK
- Can be prefixed with the database name (dbname.tblname) -> so you might need to look for dots before returning the string
- Tablename can be a derived table --> select statement in brackets. In that case there is not really a simple to tablename derive
Column names
- Column names can have aliases, not sure how you want to handle that.
- What about calculations, conditions (Case when...) --> that is not just a column name but a combined or calculated value. In that case no real column name to retrieve
- You can have also select statements for single columns in the resultset
So overall, your approach works if there is no complexity in the sql queries, if yes, your logic might not return what you want to.
What about other statements, like UPDATE and INSERT? Is that also something that you want to parse?

Related

how can i store auto increment id with string in postgress sequelize

I want to store the document id in the form of (IP-01, IP-02, IP-03) in the sequelize table. I tried to use autoincrement function on a string, but it showed an error that it can work only on INTEGER value.
I want to store it in such a form that I can store and search easily in the form of (IP-0X). So please anyone can help me
So I will give you basic idea, implementation is on you. There is no default function available using which you can autoincrement the value of string. My solution might not be the optimal way to do it but I guess it will solve your problem.
There are 2 option, you can implement some logic to achieve our required output.
On application level :-
By using count utility of sequelize you can count the total no of columns and generate your desired string.
//wherever you are creating the document add following
const count = (await Document.count()) + 1;
const newId = 'IP-' + count;
await Document.create({
id: newId,
...
});
On Database level:-
You need to create a function which will return the id string in required pattern.
CREATE OR REPLACE FUNCTION get_id()
RETURNS varchar
LANGUAGE plpgsql
AS $$
BEGIN
RETURN (SELECT 'IP-' || (SELECT count(1) from table_name) + 1);
END;
$$;
Now you just need to set default value of your id field to function call.
Before doing below changes you need to make sure that you have created get_id function in you database or else you will get function not defined error from database.
\\add this to file where you define your model
id: {
type: Sequelize.STRING,
defaultValue: Sequelize.fn('get_id')
}
Here is a demo of database level solution. LINK
EDIT
You can use one more approach instead of counting number of columns you can maintain a Sequence in your database and get the next Integer value from that.
CREATE SEQUENCE document_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1;
-- Then create your get_id function as below:
CREATE OR REPLACE FUNCTION get_id()
RETURNS varchar
LANGUAGE plpgsql
AS $$
BEGIN
RETURN (SELECT 'IP-' || nextval('document_id_seq'));
END;
$$;

Error with passing in the same variable into multiple parts of SQL statement

So I have a Node.js query that should pass in these three values and update a specific column by a specific amount for a user with a specific id:
await client.query(sqlStatement, [columnName, changeBy, id])
The 'sqlStatement' looks like:
'UPDATE tableName SET ($1) = ($1) + ($2) WHERE id = ($3)'
As you can see, it should pass in the values such that the SQL statement looks like:
'UPDATE tableName SET columnName = columnName + changeBy WHERE id = id'
I've also tried to write sqlStatement as the below (and just pass in columnName twice into the query but this also results in an error:
'UPDATE tableName SET ($1) = ($2) + ($3) WHERE id = ($4)'
The error returned is error: syntax error at or near "$1".
Not sure how to fix this--any help is much appreciated.
The reason this is happening is that node-postgres doesn't support query parameters for identifiers and your column name after SET is an identifier. Also, even if this worked, unless node-postgres somehow substituted the entire ($1) (with parentheses) for your value, you'd get
ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression.
If you wish to keep node-postgres, its documentation recommends to use pg-format in such cases, to build a dynamic SQL statement. If you consider alternatives, you can look into Knex.js that will build the queries for you, like so:
knex('tableName')
.update({
columnName: knex.raw('?? + ?',['columnName',changeBy])
})
.where('id', id)
And in the meanwhile, as a workaround, you should be able to set your sqlStatement to a dynamic SQL query on your own:
do language plpgsql $$ begin execute format('UPDATE test SET %I = %I + %s WHERE id = %s','$1','$1',$2,$3); end$$;
and try with that. Note that I removed the parentheses from around the update column to avoid the multiple-column UPDATE error. What should happen is that node-postgres evaluates the above to
do language plpgsql $$ begin execute format('UPDATE test SET %I = %I + %s WHERE id = %s','col','col',51,72); end$$;
and pass it to PostgreSQL, which should execute it as
UPDATE test SET "col" = "col" + 51 WHERE id = 72;
From the doc:
%I is for Identifiers, that PostgreSQL will automatically quote if needed.
%L is for string Literals
%s is for simple string substitution, in the sense that it will not be quoted, so it can be used for numeric literals

How can I INSERT new values and values from an already existing table?

I'm currently learning MySQL and have learned the INSERTO INTO statement and the INSERT INTO SELECT statement.
The INSERT INTO statement works like this:
INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...);
While the INSERT INTO SELECT works like this:
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;
What I'm trying to do is add new values while also adding values that I already have stored in another table.
con.query("INSERT INTO vehicles (vehicleType, vehicleModel, vehicleOwner, vehicleSpawnX, vehicleSpawnY, vehicleSpawnZ)")
From the query above, I already have the vehicleOwner value stored in another table, while the other ones I've just gotten.
How can I do this? Add a VALUES statement before SELECT?
I'm using SQL Workbench 8.0 and JavaScript. Also, all the values are NOT NULL, so I can't make two different queries, unless on the first one I add temporary values that I'll update on the second one.
What I want to replace:
vehicleType -> "players"
vehicleModel -> vehicleCreate.model
vehicleOwner -> playerID FROM players table
vehicleSpawnX -> pos.x
vehicleSpawnY -> pos.y
vehicleSpawnZ -> pos.z
Thanks!
It's not possible... But you can select data and store that on variables, then you will store it in another table
You would construct a query. Your data model is a bit hard to follow since you give no examples of the data or of what you really want to do.
So let me give a simpler example. Say, you have a table with two columns for two players and you want to put the ids into a table -- but using their names. The query would look like:
insert into pairs (playerId1, playerId2)
select p1.playerId, p2.playerId
from players p1 join
players p2
on p1.name = ? and p2.name = ?;
The ? are for parameter placeholders. I assume you know to aways use parameters when passing values into a query.

Proper insertion of table name

How does one correctly provide the table name if the name can be dynamically determined and still prevent SQL injection attacks? I am using node-postgres.
For example:
The following works but I believe is insecure:
dbclient.query("INSERT INTO " + table_name + " VALUES ($1, $2, $3)", [value_a, value_b, value_c])`
What I would like equivalently (but does not work) is:
dbclient.query("INSERT INTO $1 VALUES ($2, $3, $4)", [table_name, value_a, value_b, value_c])`
Any good library should provide proper escaping for SQL names, which include:
schema name
table name
column name
For example, within pg-promise you would use it like this:
db.query("INSERT INTO $1~ VALUES ($2, $3, $4)", [table_name, value_a, value_b, value_c])
i.e. you get your table name properly escaped by appending the variable with ~, which in turn makes it safe from SQL injection.
From here, a simple escaping for table names executed by the library:
return '"' + name.replace(/"/g, '""') + '"';
See also: SQL Names
How about having a hash let tables = {tableName1: 'table_name1', tableName2: 'table_name2'...} and then
//assuming you receive t as table name input
if(tables[t])
//build SQL query with tables[t] as the table name
else
//throw error about non-existing table
This way, you control the actual table names in the DB.
Also, do not forget to clean all input - the values may contain injections.
You might manually check the validity if table name with a regex some other validation logic. I would probably use a dictionary containing permissible table names.
var tables = {users:'users', boats:'boats'};
table_name = tables[table_name];
if (! table_name) throw new Error();
dbclient.query("INSERT INTO " + table_name + " VALUES ($1, $2, $3)", [value_a, value_b, value_c])
If you plan on generating a lot of dynamic sql, use a query builder like http://knexjs.org/
You can use escape function from pg-escape npm module to quote identifiers such as table names:
Only using escape function:
escape("INSERT INTO %I VALUES (%L, %L, %L)", table_name, value_a, value_b, value_c);
Combine it with node postgres:
dbclient.query(escape("INSERT INTO %I VALUES ($2, $3, $4)", table_name), [value_a, value_b, value_c]);

is - select * from empty table possible?

On my website I am trying to select 2 tables - tableB of which might be empty, so its not returning any results at all when tableB is empty. I hope I am explaining this properly. Any suggestions?
curatio.webdb.getAllTodoItems = function(renderFunc) {
var db = curatio.webdb.db;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM tableA, tableB", [], renderFunc,
curatio.webdb.onError);
});
}
basically TableA has e.g. Name and Surname columns and TableB has e.g. Address details. But sometimes there is no address, and then I cannot get anything to display.
I need to basically ignore tableB if it's empty.
If tableA and tableB have the same schema, you can do something like this:
SELECT * FROM tableA
UNION
SELECT * FROM tableB
However, if they don't have the same schema, you will have to do something smarter to get the union to work.
Although your query doesn't hint at this, I still think you are probably looking for a LEFT JOIN, because I'm assuming you want to link data from two tables by a common value in a column from table B (known as the foreign key).
This query selects values of the first table, even when there's no matching data in the join table:
SELECT *
FROM `tableA` `a`
LEFT JOIN `tableB` `b`
ON `b`.`someReferenceColumnToTableA` = `a`.`theReferredColumn`
Again, this assumes you meant to join two tables by some common value, for instance:
person (table A):
- id
- name
phonenumber (table B):
- id
- person_id // this is the foreign key to table A that "links" the data
- phonenumber
If you were to use a regular JOIN (also known as INNER JOIN) then only rows are returned for when both tables have matching data.
What your original query did, however, was (implicitly) CROSS JOIN all data from both tables. Given your question edit, I hardly think this is what you were actually after.
What you are performing there is called a Cross-Product of those two tables. A cross-product is essentially every row of tableA with every row of tableB. Since tableB has no rows, the cross-product has no rows. If you want all rows of both tables, use 2 queries. I would recommend reading a basic SQL tutorial.
curatio.webdb.getAllTodoItems = function(renderFunc) {
var db = curatio.webdb.db;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM tableA UNION ALL SELECT * FROM tableB", [], renderFunc,
curatio.webdb.onError);
});
}
It will best to explicitly use the column names in both select rather then using the *.

Categories