newLISP Fan Club

Forum => newLISP newS => Topic started by: Tim Johnson on June 27, 2008, 07:35:11 PM

Title: Using the MySQL API: mysql_real_escape_string - Bug?
Post by: Tim Johnson on June 27, 2008, 07:35:11 PM
There seems to be a problem with the use of the C MySQL API to

escape query strings in some of the newlisp code that I have looked at.

In the mysql5 module that comes with the distribution and also in Jeff's

own modifications, I am finding the following form in the (escape)

procedure:

(set 'safe-value (dup " " (+ 1 (length value))))

If you examine this, all this does is allocate a blank string with one more

byte than the source. There should be enough bytes to escape _all_

characters (factor of 2)- if necessary, plus a byte for the terminating

NULL byte that is standard for C. I.E. length*2+1

unless I'm wrong, the form should read:

(set 'safe-value (dup " " (+ 1 (* 2 (length value)))))

This provides the C function with twice the allocated bytes of

the source, plus a byte for the terminating NULL.

My modification shows predictable results, the original code

resulted in much strange behavior. :-)

references:

http://dev.mysql.com/doc/refman/5.1/en/mysql-real-escape-string.html

Tim

(an 'old '(in more ways than one) 'C dog)
Title:
Post by: Tim Johnson on June 29, 2008, 10:23:13 AM
Also: I recommend that Jeff Ober's changes at

http://www.artfulcode.net/articles/updating-newlisp-mysql5-module/

be merged into the mysql5 module in the distro. He's added

several features that I couldn't do without. :-)

Tim
Title:
Post by: Lutz on June 29, 2008, 11:16:40 AM
If you can help and do the merge and write some additional test routines and extend the docs, I can publish it in the newlisp.org/modules section, and when we are confident its working fine it can be put in the source and binary distributions replacing the old one. But it would be to late for 9.4.
Title:
Post by: Tim Johnson on June 29, 2008, 02:19:02 PM
Can do.



In fact, as far as I call tell, all of Jeff's changes but 'fetch-table

have been merged already. I will write a routine to test escaping

and fetch-table.

This is the first time that I have worked directly with the MySQL/c api,



Looks like there may be no need to do any _unescaping_. I posted the

subject to the Mysql mailing list and got answers that indicated that

unescaping is not necessary.



thanks

Tim
Title:
Post by: Tim Johnson on June 30, 2008, 05:17:41 PM
OK. see: http://www.johnsons-web.com/demo/newlisp/mysql-updated.lsp

To the best of my ability, I've added:

1)Two functions from Jeff Ober,

2)Modified the 'escape function with the proper memory allocation

and 'trim'ed the 'safe-value data.

3)Moved import routines into the 'init function so that the list

of shared libraries can be modified by calling code prior to

initialization.

4)Modified the test function to test both Jeff's 'fetch-table function,

and the 'escape function.



By searching on "@_THJ", you can find all of my changes.

thanks

Tim
Title:
Post by: Jeff on July 07, 2008, 06:02:47 AM
Here is a more optimized version of fetch-table, renamed to fetch-assoc:


(import libmysqlclient "mysql_fetch_field")
(define (query-fields , (field-list '()))
  (when (num-fields)
    (dotimes (i (num-fields))
      (push (get-string (get-int (mysql_fetch_field MYSQL_RES))) field-list -1))
    field-list))

(define (fetch-assoc , fields)
  (when MYSQL_RES
(setq fields (query-fields))
(map 'list fields (fetch-row))))

(define (fetch-all-assoc , fields (rows '()))
  (when MYSQL_RES
     (setq fields (query-fields))
(setq rows '())
(dotimes (i (num-rows))
  (push (map 'list fields (fetch-row)) rows -1))
rows))
Title:
Post by: Tim Johnson on July 07, 2008, 11:11:10 AM
Great. I would think that if one were to use the API, then one would

want to make the _best_ use of the API, as Jeff is doing here.



BTW: I've used rebol for years doing database programming with

mysql, on a a TCP/IP connection, rather than with libraries.



Using rebol I have coded character escaping based on

http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html,



and there seems to be some differences between docs at the

site above and the docs here:

http://dev.mysql.com/doc/refman/5.1/en/mysql-real-escape-string.html



something I need to resolve before I deploy newlisp for DB programming....
Title:
Post by: Jeff on July 07, 2008, 11:14:22 AM
Generally, just trust mysql_real_escape_string.  If you need escaping without a connection and character set doesn't matter, you can use mysql_escape_string as well.
Title:
Post by: Jeff on July 07, 2008, 11:50:35 AM
Here is a complete block that I am currently using to modify MySQL5:


(context MySQL)

(define (MySQL:query sql)
  (if MYSQL_RES (mysql_free_result MYSQL_RES))
  (set 'result  (= (mysql_query MYSQL sql) 0))
  (set 'MYSQL_RES (mysql_store_result MYSQL))
  (if (= MYSQL_RES 0) (set 'MYSQL_RES nil))
  (set 'fields (if MYSQL_RES (query-fields) nil))
  (if (and result (find "insert into" sql 1)) (set 'result (inserted-id)))
  result)

(define (fetch-assoc)
  (when MYSQL_RES
(map 'list MySQL:fields (fetch-row))))

(define (fetch-all-assoc , (rows '()))
  (when MYSQL_RES
(setq rows '())
(dotimes (i (num-rows))
  (push (map 'list MySQL:fields (fetch-row)) rows -1))
rows))

(import libmysqlclient "mysql_escape_string")
(define (simple-escape value , safe-value)
  (setq safe-value (pack (format "s%d" (+ 1 (* 2 (length value)))) " "))
  (if (string? value)
      (mysql_escape_string (address safe-value) (address value) (length value))
      (setq safe-value vaue))
  safe-value)

(import libmysqlclient "mysql_fetch_field")
(define (query-fields , (field-list '()))
  (when (num-fields)
    (dotimes (i (num-fields))
      (push (get-string (get-int (mysql_fetch_field MYSQL_RES))) field-list -1))
    field-list))

(define (sql format-string)
  (let ((format-args (cons format-string (map 'sql-value (args)))))
    (apply 'format format-args)))

(define (sql-value value)
  (case (MAIN:type-of value)
    ("string" (format "'%s'" (MySQL:simple-escape value)))
    ("symbol" (format "'%s'" (MySQL:simple-escape (name value))))
    ("integer" (format "'%d'" value))
    ("float" (format "'%f'" value))
    ("boolean" (if (true? value) "'1'" "'0'"))
    (true "NULL")))

(context MAIN)


First, query needed to be changed to set a context-global 'fields, since query-fields can only be called once per query (otherwise you get a bus error).  It clears the value on each query.



The sql-value function escapes a function as necessary for use in a sql query.  The sql function works exactly like format, but uses sql-value to escape.  These both do a simple escape.  It could easily be altered to use mysql_real_escape_string instead, but I personally needed offline escaping when I wrote it.
Title:
Post by: Tim Johnson on July 07, 2008, 12:17:48 PM
Jeff:

I found that I also had to trim the escaped string.

does
(setq safe-value (pack (format "s%d" (+ 1 (* 2 (length value)))) " "))

take care of that?

Also:
(setq safe-value vaue) ;; Typo?

Thanks

Tim
Title:
Post by: Jeff on July 07, 2008, 12:49:43 PM
Tim,



Thanks for catching that typo.  I don't know how that didn't break my code :).



Also, I believe get-string is more appropriate, since it truncates the string at the first null terminator.  I worry that trim might get valid characters if the string fills the entire array but, say, ends in a space when unpacked.



Here is a fixed version:


(import libmysqlclient "mysql_escape_string")
(define (simple-escape value , safe-value)
  (setq safe-value (pack (format "s%d" (+ 1 (* 2 (length value)))) " "))
  (if (string? value)
      (begin
        (mysql_escape_string (address safe-value) (address value) (length value))
        (setq safe-value (get-string safe-value)))
      (setq safe-value value))
  safe-value)
Title:
Post by: Jeff on July 07, 2008, 12:56:09 PM
Also, there is a typo in the MySQL module newlispdoc comments, with the function 'tables' appearing as 'table'.
Title:
Post by: Jeff on July 07, 2008, 01:30:16 PM
Sometime in the next few days (I am planning) to release a Mysql class that wraps the MySQL module using FOOP methods and allows multiple instances to safely coexist.  I'm just busy documenting everything.



PS Thanks Lutz for getting those fixes out so quickly!
Title:
Post by: Tim Johnson on July 07, 2008, 02:17:45 PM
Quote from: "Jeff"

Also, I believe get-string is more appropriate, since it truncates the string at the first null terminator.  I worry that trim might get valid characters if the string fills the entire array but, say, ends in a space when unpacked.
 

Yes!

(Just read docs for 'get-string)

Great function....