Hello All:

I was excited to hear about Mysqli prepared statements rather than using straight old style mysql statements. However i am finding these are almost unworkable given that they ask for data types and a fixed number of parameters that are to be speciied.

Even if i was to put my parameters in an array and somehow figure out a convoluted way to feed them to the prepared statements, making the process dynamic (ie not hard coded) what can one do about the "data types" for these dynamic parameters. Where does one get the "ssssisssb" values from. I have 150 fields and they are coming from an export in another database and some values are null, so i have no way of figuring out data types. Besides even if i did i would have to write convoluted code to get the data types for these fields, in a scenario where i could use my class for other tables as well.

is the above analysis correct or am i missing something?

If it is correct then i need to use old style sql. Basically then i need to protect against sql injection attacks. How can i wrap my sql statements to do this?

To summarize, the Mysqli prepared statementes seem to have several issues in that in that each of the parameters have to be specified to be bound, moreover their data type has to be listed as below. They seem more trouble than they are worth and it seems that one is sacrificing a lot and gaining a little using them. Please correct me if i am wrong.

/* Create the prepared statement */
if ($stmt = $mysqli->prepare("INSERT INTO CodeCall (FirstName, LastName) values (?, ?)")) {

/* Bind our params */
$stmt->bind_param('ss', $firstName, $lastName);

/* Set our params */
$firstName = "Jordan";
$lastName  = "DeLozier";

/* Execute the prepared Statement */
$stmt->execute();

However I have to run this code dynamically on different tables, with different parameters, each imported from a csv file, and each field has a different data type which i cannot gather from looking at the csv file (as some values are null)

Therefore mysqli prepared statements seem really restrictive and bothersome and I am thinking of just going with old mysql stlyle inserts where i can simply put in a string a fields and corresponding values and wrap in a loop and in my transaction and it will work like magic.

However this will be much slower than load in file, but much more reliable. It will be slower than mysqli queries as each query will have to be executed separately unlike in a prepared statement. And i will have to figure out how to guard against mysql injection attacks .

But it will work. Mysqli prepared statements dont seem suited at all to dyamic parameters with unknown data types.

Any suggestions?

:mad::eek:😕

    arjuna wrote:

    I have 150 fields and they are coming from an export in another database and some values are null, so i have no way of figuring out data types.

    However, don't you know the data types for the table in the database that you are importing to?

    arjuna wrote:

    Basically then i need to protect against sql injection attacks. How can i wrap my sql statements to do this?

    You still have the option of the real_escape_string() member function of the mysqli class.

    arjuna wrote:

    To summarize, the Mysqli prepared statementes seem to have several issues in that in that each of the parameters have to be specified to be bound, moreover their data type has to be listed as below.

    Those are not issues; they are the way preparing a statement works.

    arjuna wrote:

    They seem more trouble than they are worth and it seems that one is sacrificing a lot and gaining a little using them.

    You gain a great deal if the prepared statement is actually reused, otherwise you gain in terms of increased security due to the separation of the SQL statement and its data.

      laserlight;10946683 wrote:

      However, don't you know the data types for the table in the database that you are importing to?

      I am working in a dynamic scenario with any table. So i would have to get all the fields and their types and make a string "sssssiiii....n" based on the type. This is a nasty workaround the inflexibility of this feature.

      I could just use:

      INSERT into table_name (column1, column2....)
      values (value1, value2...);
      where the columns and value comma delimited strings are created from arrays created on the fly...

      laserlight;10946683 wrote:

      You still have the option of the real_escape_string() member function of the mysqli class.

      I guess this is the only option i am left with unless i want to spend lots of time writing convoluted code to get around the inflexibility of the bound parameters of prepared statements. Am I wrong? if so please tell me how...

      laserlight;10946683 wrote:

      Those are not issues; they are the way preparing a statement works.

      My point is that those are issues with theway the prepared statement works.It is useful only is very static situations, but not in situations where there a large number of indefinate fields.

      laserlight;10946683 wrote:

      You gain a great deal if the prepared statement is actually reused, otherwise you gain in terms of increased security due to the separation of the SQL statement and its data.

      Yes but at what cost? I will have to write reams of code to figure out the data type of the fields and feed them to this prepared statement. It seems like the minus points far outweigh the benefits. If it is at all possible to work around these flaws...:mad:

        arjuna wrote:

        I am working in a dynamic scenario with any table. So i would have to get all the fields and their types and make a string "sssssiiii....n" based on the type. This is a nasty workaround the inflexibility of this feature.

        I could just use:

        INSERT into table_name (column1, column2....)
        values (value1, value2...);
        where the columns and value comma delimited strings are created from arrays created on the fly...

        If you are going to assume that the data type is always a string type, then do the same for prepared statements. I am not entirely sure what will happen if you do have a type mismatch, but since you are willing to assume, assume.

        On the other hand, if you want to do it more pedantically, then you should attempt to determine the type whether or not you use prepared statements.

        arjuna wrote:

        It is useful only is very static situations, but not in situations where there a large number of indefinate fields.

        It can be used either way: without prepared statements, you would be building the SQL statement with the data within it; with prepared statements, you would be building the SQL statement with placeholders to be bound.

          laserlight;10946691 wrote:

          If you are going to assume that the data type is always a string type, then do the same for prepared statements. I am not entirely sure what will happen if you do have a type mismatch, but since you are willing to assume, assume.

          The non prepared statement way doesnt ask you to assume the data type is always a string. This is because it doesnt ask you the data type at all. Prepared statements are asking for a datatype. I cannot afford to assume such a thing as it would make my app completely unreliable. If i were to use prepared statements i would have to write lcode to determine the type of each dynamic field and create an array and feed that array to the prepared statement. The non prepared statement does not require this.

          How would i get the field types from a mysql table and given that there are so many types, i would have to write long code to ensure that each one is covered in a dynamix scenario. Is this a nasty work around or what?

          laserlight;10946691 wrote:

          On the other hand, if you want to do it more pedantically, then you should attempt to determine the type whether or not you use prepared statements.

          GIven the many mysql field types i would have to write code to
          1) Obtain field type for each field in my table (Any suggestions on how to do this?)
          2) Have a function that maps all possible mysql field types to s, i or b
          3) Create an array
          4) Feed this to the prepared statement

          😃

          laserlight;10946691 wrote:

          It can be used either way: without prepared statements, you would be building the SQL statement with the data within it; with prepared statements, you would be building the SQL statement with placeholders to be bound.

          if i were to write a ton of code and hit it with a tank, i may be able to get it to work in a scenario with a large number of fields of indefinate type from any table. It is not simply a matter of placeholders. It is how many placeholders and of what type. Arrays can be fed to the prepared statement using call_user_func_array, but then i would still have to figure out the types and make another array of those and feed it to the prepared statement.

          So much work, when it can be accomplished in a line of code with the old insert...

            As programmers we are supposed to be lazy! If we do so much work just to accomodate this feature, then thats counter productive...

            Given the rant, any recommendations about how to take a mysql table and create an array of the field types, that will then be mapped to :

            BIND TYPE COLUMN TYPE
            i All INT types
            d
            DOUBLE and FLOAT
            b BLOBs
            s
            All other types

              arjuna wrote:

              The non prepared statement way doesnt ask you to assume the data type is always a string. This is because it doesnt ask you the data type at all.

              By quoting the value, you are stating that the value is a string. In standard SQL, you are actually not supposed to use a string value as the value of a field that is not of a string type.

              Realistically, I believe that the effect of doing this with a prepared statement will be the same as if you had done it by quoting the value. I may be wrong, but you can always test and find out.

              arjuna wrote:

              So much work, when it can be accomplished in a line of code with the old insert...

              You can stop ranting and just use what you are used to. There is nothing wrong other than an increased risk of an SQL injection and a possibly slower script (on the other hand, using a prepared statement only once actually means slightly more overhead).

                arjuna;10946688 wrote:

                I am working in a dynamic scenario with any table. So i would have to get all the fields and their types and make a string "sssssiiii....n" based on the type. This is a nasty workaround the inflexibility of this feature.

                If you can query the DBMS, you can query it for information about the table in question and use that to guide the process (I'm thinking of a DatabaseTable class that can have the name of the database and table passed to it in the constructor, queries for and stores the table's setup, and also includes a method that generates a suitable INSERT statement for binding.

                This metadata about the database stored in INFORMATION_SCHEMA. It's presence is part of the SQL standard.

                  Hi Folks:

                  I have spent the last day working on this. Essentially I have got the array to bind to the mysqli prepared statement. However it seems i am doing something wrong as when i run the code it only executes the bound value in the array. I am not clear as to what is exactly getting bound in the array. Is it the index position?

                  Here is the code:

                  $params=$csv->getRow(0);
                  /* this is an array with 150 columns that i need to bind. The way this code is working now (which is wrong) is that it is inserting the first row, row(0). I tried binding by supplying field names as an array, but it inserted those as well/
                  
                       $sql="INSERT INTO $table VALUES ($strOfQuestionMarksDb)";
                       echo $sql;
                       $stmt = $dbManual->prepare($sql); 
                  
                             /* bind parameters for markers */
                             # We are trying to achieve: $stmt->bind_param("sssssssssiiiiiiiiii", $city,$fdf,$dfdf);
                             array_unshift($params, $strOfDataTypesDb);  
                             print_r ($params);             
                             $res=call_user_func_array(array(&$stmt, "bind_param"), $params);
                             echo "Did it bind the parameters " . $res;
                  
                             $stmt->execute();
                             $params=$csv->getRow(2);
                             $stmt->execute();
                             $params=$csv->getRow(3);
                             $stmt->execute();

                  My question is that when I have a 150 column array that i am trying to bind, how do i do it so that it takes the index value of the array and binds that. So that i can then iterate through the array and simply insert by using the execute() statement.

                  I can do it this way but this defeats the purpose as I would have to bind each row/record in my array and then execute it. How can i modify this so i bind once and execute in a loop?

                    Once you have your SQL string "INSERT INTO"
                    and the PARAM string "sssiiisss"
                    ... then I can not see the rest would be a problem using loops

                    Once I had the param string "ssii...." I would not make it complicated
                    Principally:

                    foreach( $row of data to insert)
                    
                    1. bind field by field one by one in a loop using foreach("ssssiiii" AS $p) or for( i=0 i<150)
                    $stmt->BindParam($p, $var);
                    where $var = $row('0', 'fielddata'), $row('1', 'fielddata'), $row('2', 'fielddata') etc
                    
                    2. $stmt->execute();
                    
                    end foreach;

                    I guess there is some hookup using this method.
                    Sure it is tremendous many loops doing this.
                    And it would take some time to run the script,
                    but I know too little of your case to see why it should not be possible.
                    Regards
                    🙂

                      Halojoy:

                      Thanks for taking the time to explain. But i am still not clear. I am considering switching to PDO as that "seems" to have a better interface for binding parameters to prepared statements.

                      Let me be a bit clearer:

                      I have an array of 150 columns and n rows of data. I have access to this array in an indexed or associative way. However I do not have variable names. Yes I can have the indexed keys or associative names.

                      I have been able to parse the data to create the data type string for the 150 columns like this "ssssssiiiiiiiiii....150"

                      Are you saying that i should use a loop to bind each parameter one at a time. In this case am i binding, the indexed value 0,1,2.. or the associative name in the array (not a variable name) Then how would i add data?

                      This is really getting confusing. Should i be looking at PDO?

                      Below is the code, it is long but i feel i should post it for the benefit of any others who might travel this path in the future to see what they are in for trying to pass parameters to a prepared statement from an array,

                      or for those who might point out how i can make this cleaner.

                      By the way, the $csv object is something i am using to get my csv file into an array. I would highly recommend it to anyone. It is awesome....But i wont go into that here, that is for the benefit of anyone trying to read the code...

                      Theres got to be a straightforward way to do this...😕😕😕😕😕

                        Here is the code for reference in case anyone needs bits of it:

                        class dataUpload 
                            {
                            protected function parseDataType($strUnparsed) 
                              {
                                $mysqlDataTypes=array ("CHAR","VARCHAR","TINYTEXT","TEXT","BLOB","MEDIUMTEXT","MEDIUMBLOB","LONGTEXT","LONGBLOB",     "TINYINT","SMALLINT","MEDIUMINT","INT","BIGINT","FLOAT","DOUBLE","DECIMAL","DATE","DATETIME","TIMESTAMP","TIME","ENUM","SET");         
                        Foreach ($mysqlDataTypes as $aMysqlDataType) { if (preg_match("/" . $aMysqlDataType ."/i", $strUnparsed)) {$inputDataType=$aMysqlDataType;}
                        } switch ($inputDataType) { case "TINYINT": case "SMALLINT": case "MEDIUMINT": case "INT": case "BIGINT": $outputDataType="i"; break; case "FLOAT": case "DOUBLE": $outputDataType="d"; break; case "BLOB": case "MEDIUMBLOB": case "LONGBLOB": $outputDataType="b"; break; default: $outputDataType="s"; break; } return $outputDataType; } protected function getFieldMetaData($whatMetaData,$dbName,$table) { #This function can be used to retrieve any meta attribute of a table field $dbManual = new mysqli('localhost', 'user', 'pass', $dbName); $sql="SHOW FIELDS FROM $table"; $result=$dbManual->query($sql); $strFieldMetaData=""; $counter=0; switch ($whatMetaData) { case "Type": while ($row = mysqli_fetch_array($result)) { if ($counter==0) {$strFieldMetaData=$this->parseDataType($row[$whatMetaData]);} else {$strFieldMetaData=$strFieldMetaData . $this->parseDataType($row[$whatMetaData]);} $counter++; $StrOfQuestionMarks=""; } $arrFieldMetaData=array ($strFieldMetaData,$counter,$StrOfQuestionMarks); break; case "Field": $StrOfQuestionMarks=""; while ($row = mysqli_fetch_array($result)) { if ($counter==0) {$strFieldMetaData="". $row[$whatMetaData]; $StrOfQuestionMarks="?";} else {$strFieldMetaData=$strFieldMetaData .",". $row[$whatMetaData]; $StrOfQuestionMarks=$StrOfQuestionMarks . "," . "?"; } $counter++; } $arrFieldMetaData=array ($strFieldMetaData,$counter,$StrOfQuestionMarks); break; default: echo "No action has been programmed to process this attribute of the table!"; break; } $result->close(); mysqli_close($dbManual); Return $arrFieldMetaData; } function doUploadUsingInserts($csvfile,$uploadDir,$table,$uniqueId,$dbName) { #Working from the perspective of the mysql database # Create the string of data types for the prepared insert statement $arrFieldMetaTypesDb=$this->getFieldMetaData('Type',$dbName,$table); $strOfDataTypesDb= $arrFieldMetaTypesDb [0]; # Create the list of field names for the prepared insert statement $arrFieldMetaNamesDb=$this->getFieldMetaData('Field',$dbName,$table);
                        $strOfFieldNamesDb=$arrFieldMetaNamesDb [0]; $strOfQuestionMarksDb=$arrFieldMetaNamesDb [2]; $noOfFieldsInTableDb=$arrFieldMetaNamesDb [1]; echo "$strOfDataTypesDb</br>"; echo "$strOfFieldNamesDb</br>"; echo "$strOfQuestionMarksDb</br>"; echo "$noOfFieldsInTableDb</br></br>"; #Working form the perspective of the csv file $csv = new File_CSV_DataSource; $csv->load($uploadDir . $csvfile); #Create a comma delimited string of field names from the csv file $arrFieldNamesCsv=($csv->getHeaders()); Foreach ($arrFieldNamesCsv as $key=>$value) { if ($key==0) {$strFieldNamesCsv=$value;} else {$strFieldNamesCsv=$strFieldNamesCsv . "," . $value;} } #Upload Validation Check #1 :Count the number of fields returned from db and compare with those in csv file #No fields in csv file $NoOfColumnsCsv=($csv->countHeaders()); #Now we get the data from the csv file #Create a comma delimited string of values for each row $arrOfValues=array (); $arrAllData=$csv->getRawArray(); $NoOfRows=count($arrAllData); $counter=0; While ($counter < $NoOfRows) { $subArray=$csv->getRow($counter); $strValuesToBeInserted=""; Foreach ($subArray as $FieldKey=>$FieldValue) { if ($FieldKey==0) {$strValuesToBeInserted=$FieldValue;} else {$strValuesToBeInserted=$strValuesToBeInserted . "," . $FieldValue;} } $arrOfValues[$counter]=$strValuesToBeInserted; $counter++; } echo "There are " . $NoOfRows . " data records in the csv file that are going to be uploaeded to the table " . $table . " in the " . $dbName . " database...</br></br>"; #Create a mysqli prepared query wrapped in a transaction to insert these values into the database table $dbManual = new mysqli('localhost', 'user', 'pass', $dbName); if (mysqli_connect_errno()) { printf("Can't connect to MySQL Server. Errorcode: &#37;s\n", mysqli_connect_error()); exit; } // turn off auto-commit mysqli_autocommit($dbManual, FALSE); #Create a mysqli prepared query wrapped in a transaction to insert these values into the database table // Bind parameters (an integer and a string). 'is' tells MySQL you're passing an integer(i) and a string(s) #Get the csv file as an associative array with keys and values where the keys are the field names #and we are trying to convert these to variables to bind to the Mysqli prepared statement $params=$csv->getRow(0); $sql="INSERT INTO $table VALUES ($strOfQuestionMarksDb)"; echo $sql; $stmt = $dbManual->prepare($sql); /* bind parameters for markers */ # We are trying to achieve: $stmt->bind_param("sssssssssiiiiiiiiii", $city,$fdf,$dfdf); array_unshift($params, $strOfDataTypesDb); print_r ($params); $res=call_user_func_array(array(&$stmt, "bind_param"), $params); echo "Did it bind the parameters " . $res; $stmt->execute(); $params=$csv->getRow(2); $stmt->execute(); $params=$csv->getRow(3); $stmt->execute(); mysqli_commit($dbManual); #$result = $dbManual->query($sql); #if ($result !== TRUE) # { # echo ("There was an error in the upload, it is being rolled back!"); # mysqli_rollback($dbManual); // if error, roll back transaction # } #else # { # echo ("The upload was successful, it has been committed!"); # // assuming no errors, commit transaction # } $dbManual->close(); }

                          Correct me if i am wrong:

                          I have to bind only once right?

                          And then execute through a loop....

                          Or do i have to bind as many times as i have records? Would this not defeat the purpose?

                            arjuna wrote:

                            I have to bind only once right?

                            And then execute through a loop....

                            Semantically, you would bind values to all the parameters, then execute. Unless you want to execute the statement with the exact same values bound to all the parameters, you would bind again, then execute, etc.

                            However, in code, you would only call bind_param() once, after which each time you assign to a bound variable, the rebinding of values to the prepared statement parameter is implied.

                              Hi All:

                              Finally got it to work. But i got to tell you it was a bit of hackery that would be unnecessary if the php mysqli prepared statements were more flexible.

                              For the powers that be it would be awesome if we could feed arrays straight to prepared statements. That would save so much trouble. Ok later when i have time, maybe i will write an object that does that. It would be nice if PHP included that in future versions as ive had to use several hacks to feed parameters to a prepared statement. Interacting with a database dynamically (without knowing the number or type of fields in the database) is very normal and standard and should be supported by future versions of php. At this time i hacked about it and it did take me a while.🆒

                              Bottom line what i did was i created a comma delimited string of variables to be bound. I then used eval to merge them into the bind statement.

                                 #Create a mysqli prepared query wrapped in a transaction to insert these values into the database table     
                              
                              #Create the following variables. I have used other code to populate these from an array. The values below are placeholders. All these variables will contain dynamic data.The values they currently have are for illustration purpose only to show what kind of values they hold. This code is completely dynamic, ie you can have any number of fields bound to the prepared statement dynamically. 
                              
                               #The function I have posted above creates this string of data types for the prepared statement	
                               $strOfDataTypesDb='si';
                              
                               # Note the single quotes here. I had initially put double quotes which evaluated 
                               #these variables, giving me an error in my prepared statement as these are the parameters to be bound. 
                               #This variable holds the name of the variables to be bound in the prepared statement
                               # It is in a sense a meta variable. A variable of variables as a comma delimited string. 
                              
                               $strOfFieldNamesDbWDollars='$Subcentre,$SerialNo';
                              
                              #These are my field names in the data base. A function posted above gets these dynamically 
                              $strOfFieldNamesDb="Subcentre,SerialNo";
                              
                              #This is the string of question mark placeholders  
                              $strOfQuestionMarksDb="?,?";         
                              
                              
                              $dbManual = new mysqli('localhost', 'user', 'pass', $dbName);  
                                    if (mysqli_connect_errno())
                                      {
                                      printf("Can't connect to MySQL Server. Errorcode: %s\n", mysqli_connect_error());
                                      exit;
                                      }
                                   // turn off auto-commit
                                   mysqli_autocommit($dbManual, FALSE);
                              
                                   #Create a mysqli prepared query wrapped in a transaction to insert these values into the database table           
                              
                              #The below statement gets my data. In your case $params is an indexed array (non associative) that holds your data 
                              $params=$csv->getRow(0);
                              
                                   $sql="INSERT INTO $table ($strOfFieldNamesDb) VALUES ($strOfQuestionMarksDb)";
                                   $stmt = $dbManual->prepare($sql);         
                              
                              #The eval statement executes the bind statement, but dynamically inserts the field names to be bound. 
                              #This is another key aspect        
                                   $evalString='$stmt->bind_param($strOfDataTypesDb,'. $strOfFieldNamesDbWDollars . ');';
                                   echo $evalString;   
                                   eval($evalString);     
                              
                              #This is an associative array of the data
                                  $paramsWithValues=$csv->connect(); 
                                  #Extract the field names to variables
                                  extract($paramsWithValues [0], EXTR_PREFIX_SAME, 'prefix');
                              
                                  extract($paramsWithValues [0]);             
                                  $stmt->execute();
                              
                                  extract($paramsWithValues [1]);             
                                  $stmt->execute();
                              
                                  extract($paramsWithValues [2]);             
                                  $stmt->execute();
                                  mysqli_commit($dbManual);
                              

                              🙂

                                Write a Reply...