CREATE TABLE fails when i use an integer name for column - php

I'm trying to create a dynamic form builder. Therefore PHP gets a set of names from a database and creates a new table with those names as column names. This works quite well until one or more names are integer (for ex. '12345'). Then the script fails.
How can I force PHP of MySQL to give numeric table names?
Here is a piece of the code (its still a draft):
$slaop = 'id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)';
require_once 'dbconnect.php';
$connector = new DbConnector();
$result = $connector->query( 'SELECT * FROM '.$form.' ORDER BY rang' );
while ($opslaan = $connector->fetchArray($result)){
$slaop .= ', ';
$slaop .= $opslaan['tekstid'];
$slaop .= ' TEXT';
}
echo $slaop;
// OPSLAAN
$formnaam = $_GET['welkform'];
require_once 'dbconnectsave.php';
$connector = new DbConnectorSave();
$connector->query('CREATE TABLE '.$formnaam.'('.$slaop.')')
or die(mysql_error());
$opslaan['tekstid']; is the part where the text of integer are called.
Does anyone have an idea?

Why not prefix all tables with the form name? Then integers don't matter...

As others have noted, this may not be a good idea.
However, you can still do it if the column name is surrounded in backticks. Here's a couple of MySQL examples:
create table abc (id int, 123 int); -- fails
create table abc (id int, `123` int); -- succeeds

How can i force php of mysql to give numeric table names?
May be you can force your application not to use such field names?
And also change the whole design as well, without employing dynamically created tables, and use more usual approach of storing table structure in some table?

Use a prefix that starts with a letter for your column names.

Related

Create table of BBDD #variable

I would like to create a Table in a BBDD by variable in MYSQL. The problem i have is that the code doesn't work and I am not sure why.
$variable = "xxx_'".$_POST['idtour']."'_xxxx";
// Create the table
$sql = "CREATE TABLE $tourname (
id_leg VARCHAR(3) NOT NULL COMMENT 'Identificación de la leg del tour, en orden')";
//The action
mysqli_query($link,$sql) or die("Error ".mysqli_error());
I think is a problem with the "_".Any help will be fantastic.
The problem isn't the _. It's the '. "xxx_'".$_POST['idtour']."'_xxxx" will result in xxx_'value'_xxxx, as a possible table name, and ' is not valid in a table name. If you MUST include invalid characters, then you'll need to use the backtick operator.
$tablename = "`xxx_'".$var."'_xxxx`";
That should get you further towards your goal.
On a side note: creating a table based on a user-provided variable is a bad idea. It risks users being able to create some bizarre and destructive behavior, and very often it's better accomplished by adding a column to an existing table. Have you tried adding a column user_idtour?

php files on the fly

In PHP, If i have this assign:
$link = 'Home';
And in another php section i need to grab the value of $link and insert it in a MYSQL query in plain text, like this:
$sql = "SELECT txt FROM Home WHERE id = 1";
Note that $link has been written in the format of it's value in the query.
Basically i need to see the inside of the $link var and write it down on the query.
Why i need this -> because i'm creating php files on the fly after the click of a submit button.
And inside of the newly created file is a tinymce text editor that needs to read it's text contents from a table that was also created on the fly along with this file.
What you want to do is this:
$sql = "SELECT txt FROM $link WHERE id = 1";
^ ^ ^
The double-quotes (") specify that the string has to be interpreted (with single quotes this won't work). Inside the string you can then write the name of the variable ($link) and it will be "supplanted" by its value.
I would'nt recommend doing this because it is vulnerable to SQL-injection and it is a very very bad practice that show that you do things in an uncommon way. Better avoid it!
EDIT: even worse...
When you say in a comment creates a mysql table with the name of the file that's an even worse thing to do. First, not all characters in a filename should go into a table name. Second, you can get litearlly anything as a filename, even things that are not actual filenames.
It is possible to forge a HTTP request so that the filename is an arbitrary string of your liking, for example:
a (); SELECT * FROM accounts; --
When you put this string into your query:
CREATE TABLE $filename (id int PRIMARY KEY, whatever varchar(20) NOT NULL);
You get this resulting SQL query:
CREATE TABLE a (); SELECT * FROM accounts; -- (id int PRIMARY KEY, whatever varchar(20) NOT NULL);
As you can see, this will create a table a without any columns and then select all usernames and passwords, if the table accounts exists. The rest of the query is commented out with --.
This is a very simple SQL injection attack and you don't want to do this! Don't put userinput straight up into your SQL queries!!
EDIT: taking the value from $_POST
If your field has the name link then you can access it with
$_POST['link']
You can put this into your string this way:
$sql = "SELECT txt FROM {$_POST['link']} WHERE id = 1";
Do you want something like this:
$sql = "SELECT txt FROM ".$link." WHERE id = 1";

update cell value if value not already in

I have to update the column File on the TABLE TEST. This column contains the files related to the row. Each file is separated by a |.
An example could be
ID NAME FILE
1 apple fruit.png | lemon.png
Now when I add a new file to the FILE column I use this query:
$link->query("UPDATE TEST SET File = CONCAT(File, '$dbfilename') WHERE id = '$p_id'")
where $dbfilename can be e.g. pineapple.jpg |
The problem is that, if $dbfilename is already on the File values, it will be added another time, resulting double.
How can I check if File contains already $dbfilename, and if yes, don't add id, or even don't execute the query?
This is not a good way of storing information in a database. But I'll get to that in a second. To directly answer your question, you could use this as your SQL query:
UPDATE TEST SET File = CONCAT(File, '$dbfilename')
WHERE id='$p_id'
AND File NOT LIKE '%$dbfilename%'
AND Lingua='$linguadilavoro'
However, this may cause some issues when one file pineapple.jpg and you try to add another-pineapple.jpg
Really, I think you should consider how this is a horribly bad approach to databases. Consider breaking the files off into a second table. For example:
# a table for the fruit names
CREATE TABLE fruits (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(250) NOT NULL,
UNIQUE INDEX(name)
);
# a table for file names
CREATE TABLE files (
fileid INT UNSIGNED NOT NULL DEFAULT PRIMARY KEY AUTO_INCREMENT,
fruitid INT UNSIGNED,
filename VARCHAR(250),
UNIQUE INDEX(fruitid, filename)
);
# find all of the fruits with their associated files
SELECT fruits.id, fruits.name, files.filename
FROM fruits LEFT JOIN files ON fruits.id=files.fruitid
# add a file to a fruit
INSERT INTO files (fruitid, filename)
VALUES ('$fruitID', '$filename')
ON DUPLICATE KEY UPDATE fruitid=LAST_INSERT_ID(id)
You will have to select out the FILE for the id.
then use explode to break it into an array
then check use in_array to determine if it should be added or not
Here is some (untested) code for guidance
$stmt = $link->query("SELECT File File from TEST WHERE id = '$p_id'");
$rec = $stmt->fetchAssoc();
$files = explode(" | ",$rec["FILE"]);
if (!in_array($dbfilename, $files)){
// add to FILE
} else {
// it's already there
}
I would redesign your table structure and add a new table File with the following columns instead of using a varchar field for multiple values:
Table Test
TableId, Name
Table File
FileId, TestId, FileName

how to add prefix in auto generated field value in database table?

we have a auto-generated field in database table and we want to add prefix in auto-generated value Like AVL0001.
So you could do it a couple ways... Is this an auto-index field in the database? If it is an integer type, then you won't be able to include data like you are mentioning above, however, if it is something generated from a script, just concatenate the number and your prefix before insert. You could probably also do this with a trigger on the database. Any additional details would help improve this answer.
$currentdbvalue = 'example';
$prefix = 'AVL0001';
$newvalue = $prefix.$currentdbvalue;
outputs "AVL0001example"
or if you'd like an underscore u can use:
$newvalue = $prefix."_".$currentdbvalue;
which would output "AVL0001_example"
Let id be your table column. You can add AVL ahead of your id by concatenating both in your sql query.
ie In Mysql,
$yourid="1";
INSERT INTO table( id )
VALUES (
CONCAT( "AVL", $yourid, id )
)
Or you can concatenate the AVL with yourid before inserting it into database like,
$yourid="AVL"."1";
In either way you cannot add it into an auto incrementing field. Because its type is INT.

How to generate unique id in MySQL?

I'm programming a script using PHP and MySQL and I want to get a
unique id (consisting of a string: capitals and small
letters with numbers) like: gHYtUUi5b.
I found many functions in PHP that can generate such numbers but I'm afraid about how to ensure the id is unique!
UPDATE: uuid is long, I mean such id like: (P5Dc) an 11 alphanumeric char.
EDIT: This answer has been flagged for being dangerous in the context of destroying a database. Do NOT use this code to generate unique ids in databases!
I use UUID() to create a unique value.
example:
insert into Companies (CompanyID, CompanyName) Values(UUID(), "TestUUID");
You may like the way that we do it. I wanted a reversible unique code that looked "random" -a fairly common problem.
We take an input number such as 1,942.
Left pad it into a string: "0000001942"
Put the last two digits onto the front: "4200000019"
Convert that into a number: 4,200,000,019
We now have a number that varies wildly between calls and is guaranteed to be less than 10,000,000,000. Not a bad start.
Convert that number to a Base 34 string: "2oevc0b"
Replace any zeros with 'y' and any ones with 'z': "2oevcyb"
Upshift: "2OEVCYB"
The reason for choosing base 34 is so that we don't worry about 0/O and 1/l collisions. Now you have a short random-looking key that you can use to look up a LONG database identifier.
A programmatic way can be to:
add a UNIQUE INDEX to the field
generate a random string in PHP
loop in PHP ( while( ! DO_THE_INSERT ) )
generate another string
Note:
This can be dirty, but has the advantage to be DBMS-agnostic
Even if you choose to use a DBMS specific unique ID generator function (UUID, etc)
it is a best practice to assure the field HAS to be UNIQUE, using the index
the loop is statistically not executed at all, it is entered only on insert failure
If you use MySQL with version higher than 5.7.4, you can use the newly added RANDOM_BYTES function:
SELECT TO_BASE64(RANDOM_BYTES(16));
This will result in a random string such as GgwEvafNLWQ3+ockEST00A==.
How you generate the unique_ids is a useful question - but you seem to be making a counter productive assumption about when you generate them!
My point is that you do not need to generate these unique id's at the time of creating your rows, because they are essentially independent of the data being inserted.
What I do is pre-generate unique id's for future use, that way I can take my own sweet time and absolutely guarantee they are unique, and there's no processing to be done at the time of the insert.
For example I have an orders table with order_id in it. This id is generated on the fly when the user enters the order, incrementally 1,2,3 etc forever. The user does not need to see this internal id.
Then I have another table - unique_ids with (order_id, unique_id). I have a routine that runs every night which pre-loads this table with enough unique_id rows to more than cover the orders that might be inserted in the next 24 hours. (If I ever get 10000 orders in one day I'll have a problem - but that would be a good problem to have!)
This approach guarantees uniqueness and takes any processing load away from the insert transaction and into the batch routine, where it does not affect the user.
Use UUID function.
I don't know the source of your procedures in PHP that generates unique values. If it is library function they should guarantee that your value is really unique. Check in documentation. You should, hovewer, use this function all the time. If you, for example, use PHP function to generate unique value, and then you decide to use MySQL function, you can generate value that already exist. In this case putting UNIQUE INDEX on the column is also a good idea.
DELIMITER $$
USE `temp` $$
DROP PROCEDURE IF EXISTS `GenerateUniqueValue`$$
CREATE PROCEDURE `GenerateUniqueValue`(IN tableName VARCHAR(255),IN columnName VARCHAR(255))
BEGIN
DECLARE uniqueValue VARCHAR(8) DEFAULT "";
DECLARE newUniqueValue VARCHAR(8) DEFAULT "";
WHILE LENGTH(uniqueValue) = 0 DO
SELECT CONCAT(SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1)
) INTO #newUniqueValue;
SET #rcount = -1;
SET #query=CONCAT('SELECT COUNT(*) INTO #rcount FROM ',tableName,' WHERE ',columnName,' like ''',newUniqueValue,'''');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
IF #rcount = 0 THEN
SET uniqueValue = #newUniqueValue ;
END IF ;
END WHILE ;
SELECT uniqueValue;
END$$
DELIMITER ;
And call the stored procedure as GenerateUniqueValue('tableName','columnName'). This will give you a 8 digit unique character everytime.
To get unique and random looking tokens you could just encrypt your primary key i.e.:
SELECT HEX(AES_ENCRYPT(your_pk,'your_password')) AS 'token' FROM your_table;
This is good enough plus its reversable so you'd not have to store that token in your table but to generate it instead.
Another advantage is once you decode your PK from that token you do not have to do heavy full text searches over your table but simple and quick PK search.
Theres one small problem though. MySql supports different block encryption modes which if changed will completely change your token space making old tokens useless...
To overcome this one could set that variable before token generated i.e.:
SET block_encryption_mode = 'aes-256-cbc';
However that a bit waste... The solution for this is to attach an encryption mode used marker to the token:
SELECT CONCAT(CONV(CRC32(##GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token' FROM your_table;
Another problem may come up if you wish to persist that token in your table on INSERT because to generate it you need to know primary_key for the record which was not inserted yet... Ofcourse you might just INSERT and then UPDATE with LAST_INSERT_ID() but again - theres a better solution:
INSERT INTO your_table ( token )
SELECT CONCAT(CONV(CRC32(##GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = "your_table";
One last but not least advantage of this solution is you can easily replicate it in php, python, js or any other language you might use.
Below is just for reference of numeric unique random id...
it may help you...
$query=mysql_query("select * from collectors_repair");
$row=mysql_num_rows($query);
$ind=0;
if($row>0)
{
while($rowids=mysql_fetch_array($query))
{
$already_exists[$ind]=$rowids['collector_repair_reportid'];
}
}
else
{
$already_exists[0]="nothing";
}
$break='false';
while($break=='false'){
$rand=mt_rand(10000,999999);
if(array_search($rand,$alredy_exists)===false){
$break='stop';
}else{
}
}
echo "random number is : ".$echo;
and you can add char with the code like -> $rand=mt_rand(10000,999999) .$randomchar; // assume $radomchar contains char;
For uniqueness what I do is I take the Unix timestamp and append a random string to it and use that.
<?php
$hostname_conn = "localhost";
$database_conn = "user_id";
$username_conn = "root";
$password_conn = "";
$conn = mysql_pconnect($hostname_conn, $username_conn, $password_conn) or trigger_error(mysql_error(),E_USER_ERROR);
mysql_select_db($database_conn,$conn);
// run an endless loop
while(1) {
$randomNumber = rand(1, 999999);// generate unique random number
$query = "SELECT * FROM tbl_rand WHERE the_number='".mysql_real_escape_string ($randomNumber)."'"; // check if it exists in database
$res =mysql_query($query,$conn);
$rowCount = mysql_num_rows($res);
// if not found in the db (it is unique), then insert the unique number into data_base and break out of the loop
if($rowCount < 1) {
$con = mysql_connect ("localhost","root");
mysql_select_db("user_id", $con);
$sql = "insert into tbl_rand(the_number) values('".$randomNumber."')";
mysql_query ($sql,$con);
mysql_close ($con);
break;
}
}
echo "inserted unique number into Data_base. use it as ID";
?>
crypt() as suggested and store salt in some configuration file, Start salt from 1 and if you find duplicate move to next value 2. You can use 2 chars, but that will give you enough combination for salt.
You can generate string from openssl_random_pseudo_bytes(8). So this should give random and short string (11 char) when run with crypt().
Remove salt from result and there will be only 11 chars that should be enough random for 100+ millions if you change salt on every fail of random.
You might also consider using crypt()* to generate a [nearly-guaranteed] unique ID inside your contraints.
USE IT
$info = random_bytes(16);
$info[6] = chr(ord($info[6]) & 0x0f | 0x40);
$info[8] = chr(ord($info[8]) & 0x3f | 0x80);
$result =vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($info), 4));
return $result;
This generates random ids:
CREATE TABLE Persons (
ID Integer PRIMARY KEY AUTOINCREMENT,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Age int
);
You could use Twitter's snowflake.
In short, it generates a unique id based on time, server id and a sequence. It generates a 64-bit value so it is pretty small and it fits in an INT64. It also allows for sorting values correctly.
https://developer.twitter.com/en/docs/basics/twitter-ids
In sum, it allows multiple servers, highly concurrency, sorting value and all of them in 64 bits.
Here it is the implementation for MySQL
https://github.com/EFTEC/snowflake-mysql
It consists of a function and a table.

Categories