Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusable Prepare Statements on MySQL (plus basic support for postgres and sqlite3) #100

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

fcr--
Copy link

@fcr-- fcr-- commented Nov 24, 2018

This pull request is based on the PR #99, however this one adds additional support for reusing prepared statements.

Example:

local driver = require 'luasql.mysql'.mysql()

local conn = assert(driver:connect('test'))
assert(conn:setautocommit(false))
assert(conn:execute 'create table if not exists numbers(n int)')
assert(conn:execute 'truncate table numbers')

local t = os.time()
local stmt = assert(conn:prepare('insert into numbers values(?)'..(',(?)'):rep(9)))
for i = 0, 999 do
  for j = i*1000 + 1, i*1000 + 1000, 10 do
    assert(stmt:execute(j, j+1, j+2, j+3, j+4, j+5, j+6, j+7, j+8, j+9))
  end
  print((i+1)*1000 .. ' rows inserted.')
end
assert(conn:commit())
print('inserted a million rows in about ' .. os.time() - t .. ' seconds')

fcr-- added 8 commits May 31, 2018 02:24
With this modification we can enjoy the security benefits of having
prepared-statement-alike additional parameters.  To do this, the
additional parameters should be passed after the statement in the
execute method.

This means that a new prepared statement will be created on each
execute call, so don't expect big a performance increase.

Maybe in a distant future a LRU cache of prepared statements could be
added.
As in SQLite3, we now support prepared-statements-alike passing of
optional parameters by using the PQexecParams function, and since adding
support for binary types is not an easy task, any argument is converted
to a string before being sent and converted back to the expected type by
PostgreSQL... in any case this is better than nothing.

You may want to use a cast ``::type'' if it's not inferred. Example:

> db = require'luasql.postgres'.postgres():connect('')
> assert(db:execute('create table t(a int)'))
> assert(db:execute('insert into t values($1)', 17))
> res = assert(db:execute('select $1+$2::int, a from t where a>$1', 3, 4))
> =res:fetch()
7	17
  Since sqlite3_bind_text is binary safe, binding as text can be done
  without worries.
  This eliminates the need for escaping parameters, since now they
  can be specified as additional arguments to execute.
  This function can be used by all the drivers simplifying
  the logic, requiring only to implement conn:prepare(sql)
  and stmt:execute(...).
@blumf
Copy link
Contributor

blumf commented Nov 27, 2018

Could you add some test cases to the relevant drivers? Not sure if it's worth putting the tests into the main test script or keeping them separate for now.

Got the ODBC working to match. Will be working on Firebird next.

@fcr--
Copy link
Author

fcr-- commented Nov 27, 2018

I've added them as another extension, so it wouldn't interfere with the other drivers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants