Lua: Connect with MSSQL DB - database

I'm learning about Lua (version 5.3),Is there a way to connect to an mssql database?
I try to do it as follows
print("Connection to the db:")
local dbuser = '....'
local dbpass = '...'
local dbsource = '....'
local dbname = 'xx.xx.xxx.xx'
database.open("Provider= ODBC ; Initial Catalog = dbname; Data Source = dbsource ; User ID = dbuser ; Password = dbpass ")
print("Database opened succesfully.")
print("Connection to the db:")
But didn´t work, execution returns me:
attempt to index a nil value (global 'database')
Thanks,

I try to do it as follows
database.open("Provider= ODBC ; Initial Catalog = dbname; Data Source = dbsource ; User ID = dbuser ; Password = dbpass ")
What made you think you can do that? You get that error because there is no global variable database unless you define one.
Hence you may not index it. Trial and error with random code is not a very good way to get things done in programming.
Also you cannot handle strings like that in Lua. You either need to use string.format or the concatenation operator .. to get your variables into that string.
There's also no printf in Lua unless you define it.
I suggest you learn the basics of Lua befor you get into interfacing databases.
From reading the Lua reference manual it should become obvious that Lua does not know anything about databases.
Either you run Lua embedded into some host application that provides database access through its Lua API or you need to load a library that supports that.
Searching the web for "lua sql" instantly yields
https://keplerproject.github.io/luasql/
and others.

Related

Query data from SQL to MS Access: Local Tables vs Pass-Through Tables

I've created an application that uses the following logic to query data from SQL to my MS Access App.
Using an ODBC connection I execute a stored procedure
Using This is assigned as a Pass-Through Query to pull the data locally.
It looks something like this:
strSQL = "EXEC StoredProcedure " & Variable & "
Call ChangeQueryDef("qryPassThrough", strSQL)
Call SQLPassThrough(strQDFName:="qryPassThrough", _
strSQL:=strSQL, strConnect:=gODBCConn)
Me.frmDataSheet.Form.RecordSource = "qryPassThrough"
But, recently we have upgraded our SQL Server to 2016 using a high availability failover system - hence our connection string has changed to connect to a listener like so:
gODBCConn = "ODBC;Driver= {SQL Server Native Client 11.0};Trusted_Connection=Yes;Regional=Yes;Database=" & varDB & ";MultiSubnetFailover=Yes;IntegratedSecurity=SSPI;Server=tcp:SERVER_LISTENER,1433;"
However, it looks like using SQL Server Native Client in the connection string is not the same as what we originally had which was SQL Server. Certain data types have changed and do not work in Access.
Is there a better way for me to query data from SQL and persist/display this data in access using ADO or an alternative method?
EDIT Based on Comment:
The issue I'm having is that I have tables in SQL using the data type: Decimal(12,2). With some testing and experimenting this seems to fail when using an ODBC pass-through query. But changing the data type to Float seems to work fine. Then there are other datatypes which seem to error too which I've not managed to find yet. It just seems there are a few difference which I'm not aware of and I'm keen to find a better way to load data into my Access App.
EDIT 2
This is the error message I get relating to the data type issue.
Sounds like you're not really interested in making the underlying data structure compatible with Access, so:
How to load an ADODB recordset into a datasheet form
Create the form
First, create a datasheet form. For this example, we're going to name our form frmDynDS. Populate the form with 256 text boxes, named Text0 to Text255. To populate the form with the text boxes, you can use the following helper function while the form is in design view:
Public Sub DynDsPopulateControls()
Dim i As Long
Dim myCtl As Control
For i = 0 To 255
Set myCtl = Application.CreateControl("frmDynDS", acTextBox, acDetail)
myCtl.NAME = "Text" & i
Next i
End Sub
VBA to bind a recordset to the form
First, we're going to allow the form to persist, by allowing it to reference itself:
(all on in the code module for frmDynDS)
Public Myself As Object
Then, we're going to add VBA to make it load a recordset. I'm using Object instead of ADODB.Recordset to allow it to both take DAO and ADODB recordsets.
Public Sub LoadRS(myRS As Object)
Dim i As Long
Dim myTextbox As textbox
Dim fld As Object
i = 0
With myRS
For Each fld In myRS.Fields
Set myTextbox = Me.Controls("Text" & i)
myTextbox.Properties("DatasheetCaption").Value = fld.NAME
myTextbox.ControlSource = fld.NAME
myTextbox.ColumnHidden = False
i = i + 1
Next fld
End With
For i = i To 255
Set myTextbox = Me.Controls("Text" & i)
myTextbox.ColumnHidden = True
Next i
Set Me.Recordset = myRS
End Sub
Use the form
(all in the module of the form using frmDynDS)
As an independent datasheet form
Dim frmDS As New Form_frmDynDS
frmDS.Caption = "My ADO Recordset"
frmDS.LoadRS MyAdoRS 'Where MyAdoRS is an open ADODB recordset
Set frmDS.Myself = frmDS
frmDS.Visible = True
frmDS.SetFocus
Note that you're allowed to have multiple instances of this form open, each bound to different recordsets.
As a subform (leave the subform control unbound)
Me.MySubformControl.SourceObject = "frmDynDS"
Me.MySubformControl.Form.LoadRS MyAdoRS 'Where MyAdoRS is an open ADODB recordset
Warning: Access uses the command text when sorting and filtering the datasheet form. If it contains a syntax error for Access (because it's T-SQL), you will get an error when trying to sort/filter. However, when the syntax is valid, but the SQL can't be executed (for example, because you're using parameters, which are no longer available), then Access will hard crash, losing any unsaved changes and possibly corrupting your database. Even if you disable sorting/filtering, you can still trigger the hard crash when attempting to sort. You can use comments in your SQL to invalidate the syntax, avoiding these crashes.
You previously used the pretty ancient, original ODBC Driver for SQL Server simply named SQL Server. You made the right decision to use a newer driver to support your failover cluster. But I would not recommend to use SQL Server Native Client. Microsoft says, It is not recommended to use this driver for new development.
Instead I would use the Microsoft ODBC Driver 13.1 for SQL Server. This is the most recent and recommended (by Microsoft) ODBC Driver for SQL Server.
Your main issue seems to be a translation issue between Access and SQL Server via the ODBC layer. So, using the more modern driver might very well make this problem go away. - I do not know if it solves your problem, but this is the very first thing I would try.

Excel data connection to SQL dB error

I crafted a macro in an Excel workbook to extract a subset of data from a SQL database based on user input.
The macro prompts the user for a parameter input and inserts that parameter into a ready-made stored procedure configured into a an Excel data connection - see below for my vba:
Sub RefreshDBQuery()
Dim Val As Integer
Application.ScreenUpdating = False
Worksheets("Adjustable CF").Select
Val = InputBox("Enter valid 4 digit number", , 1907)
Sheets("TestData").Visible = True
Worksheets("TestData").Select
Worksheets("TestData").Range("A1").Select
ActiveCell.Value = Val
With ActiveWorkbook.Connections("MacroExtraction 2Server").OLEDBConnection
.CommandText = "EXEC dbo.prV_FlowExtract '" & Range("A1").Value & "'"
End With
ActiveWorkbook.Connections("MacroExtraction 2Server").Refresh
Sheets("TestData").Visible = False
End Sub
When I run it - it works fine and additionally, since it's modifying an existing data connection ( the one I previously configured), I notice a odc file in a folder called "My Data Sources" under My Documents:
However, when I send this workbook over to a colleague to run the macro and to extract data - the macro is able to run up to a point, and she receives an error:
I ask her to open up the folder "My Data Sources" and I don't see an odc file:
My question is: what am I missing? Or rather what is my colleague missing in order to get her macro to work on her local machine?
I checked with the dB administrator who said that she has the permissions necessary to access the server, so that's why I am picking on the lack of the odc as a cause for my concern. Should I copy my odc file and send it to her to copy into her Data Sources folder? Should I rewrite the macro and re-setup the data connection on her local machine? Anyone with experience to comment would be much appreciated! Thanks!
The macro alone does not contain all the necessary info (server name, for example?). Try running "NewSQLServerConnection.odc" in you colleague's my data sources location, complete the necessary data, make sure the connection name is the same as in your macro, and then the macro should work.
Hope this helps!

DSN-less Connection MS Access front end and SQL server backend

I don't normally program in MS Access VBA so forgive my question if it's stupid.
So I'm using MS Access 2010 as a front end and SQL Server 2014 as a backend. (I don't have a choice in frontend interface so please no suggestions on alternate options).
I'd like to programatically link SQL server's backend to my MS Access frontend. I read here at DJ Steele's DSN-less connection page that I can use the code he provided here to make a DSN-less connection to SQL server as a backend.
So I copied that into a VBA Access module and opened another module and ran this code to run the DJ Steele code in an attempt to connect to a small SQL Server database I made:
Option Compare Database
Sub runThis()
FixConnections "AServerNameHere", "MS_Access_BackEnd_Test"
End Sub
As far as I can tell from the VBA debugger it gets to
Set dbCurrent = DBEngine.Workspaces(0).Databases(0)
then that value seems to be empty. I'm not sure how else to proceed with this since as far as I was able to find this is one of the few full examples of a DSN-less connection I could find.
I'd like to not use the DSN method of linking a SQL server to a database since that would require me to go visit people and their computers in order to make the links. (And who'd want to to that? LOL)
I've also looked at similar questions that were linked to me while writing this question and this was close to what I wanted, but it kept giving me "Compile error: Constant expression required" for input of:
LinkTable "MS_Access_BackEnd_Test", "Table_1"
and
LinkTable "MS_Access_BackEnd_Test", "Table_1", , "AServerNameHere"
Again I'm not familiar with MS Access VBA so forgive the question if it's lame.
Looking at DJ Steele's code, I got it working apart from the line
' Unfortunately, I'm current unable to test this code,
' but I've been told trying this line of code is failing for most people...
' If it doesn't work for you, just leave it out.
tdfCurrent.Attributes = typNewTables(intLoop).Attributes
which I had to comment out.
Using Set dbCurrent = CurrentDb() does essentially the same thing as Set dbCurrent = DBEngine.Workspaces(0).Databases(0) but the latter is meant to be a lot faster ... which these days means it takes 10 microseconds instead of 100 :-o
You still need dbCurrent as a reference to the current Access front end which is where the linked table objects live, even if the data is coming from elsewhere.
Edit: working for me
I added a debug.print line to monitor what's going on
' Build a list of all of the connected TableDefs and
' the tables to which they're connected.
For Each tdfCurrent In dbCurrent.TableDefs
Debug.Print tdfCurrent.Name, tdfCurrent.Connect
If Len(tdfCurrent.Connect) > 0 Then
If UCase$(Left$(tdfCurrent.Connect, 5)) = "ODBC;" Then
...
and then
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
FixConnections "TESTXP\SQLEXPRESS", "TacsData"
MSysAccessObjects
MSysAccessXML
...
MSysRelationships
Table1
dbo_Person ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DRIVER=sql server;SERVER=TESTXP\SQLEXPRESS;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes

Specify and use multiple libraries in ODBC connection string

My odbc connection string for connecting to DB2i looks like this:
Driver={Client Access ODBC Driver (32-bit)};system=xx.xx.xx.xx;dbq=LIB1 LIB2 LIB3 LIB4 LIB5 LIB6 LIB7 LIB8;languageid=ENU;qrystglmt=-1;cmt=0;signon=1
The above connection string specifies multiple libraries/schemas for use. But when I try to access a file/table from a library other than the first one (like from LIB2...LIB8) I get a exception saying "FILE xx not found in LIB1"
Why does not it automatically search for the file/table in other specified libraries, it searches for the file in the first library only?
I need a workaround for this situation.
Use "system naming" mode, by adding naming=1 to your connection string.
In your library list, place a comma before the first library.
Driver={Client Access ODBC Driver (32-bit)};system=systemname;naming=1;
dbq=,LIB1,LIB2,LIB3,LIB4,LIB5,LIB6,LIB7,LIB8;languageid=ENU;cmt=0;signon=1
This works as documented in the manual:
The library list is used for resolving unqualified stored procedure calls and finding libraries in catalog API calls. ...
Note:
The first library listed in this property will also be the default library, which is used to resolve unqualified names in SQL statements.
As stated above, Schema/library list is used to resolve functions/procedure names, not tables.
Let assume you need to read data from lib1.tab1 and lib2.tab2;
Here my personal workarounds (from easy to complex):
a) ask the db admin to have - for each table you need to use - the corresponding schema name, then do "select * from lib1.tab1 join lib2.tab2 on [...]" ;-)
b) ask the db admin to create on schema "MyAlias" several alias (create alias) for each table you want to use. Then do "set current schema=MyAlias" followed by all the SQL statement you need e.g. "select * from tab1 join tab2". Since you’re querying myalias.tab1 which is an alias pointing to table lib1.tab1 it should work.
c) Complex: create your own SQL function that returns the corresponding schema_name for a table (e.g. myfunct('TAB1'). This could be done reading system view “qsys2.systables” where table_name=’TAB1’ and returning TABLE_SCHEMA column, which is a varchar(128). Once you got it, build up a dynamically prepared using the variable you just obtained. E.g.
"set mylib = myfunct('TAB1').
"set mystmt = 'select * from '||table_schema || ‘.tab1’ …”
Prepare mystmt and then execute mystmt.
I did something similar in VBA using ado ibmdrda and it worked.
hope this helps.
f.

SQL Server: Parameter is not replaced

I've got a fully functionable (secure) session to a SQL Server database (version 10.50.4000). This is stored in a public variable:
SqlConnection conn = new SqlConnection();
I only want to run SELECT queries. For anything else, the user account got no rights.
The queries are built with only one user entry, which is inserted into a simple Text Box.
Unfortunately I must not tell you the original command text. So I make it simple for you:
function print_users(string filtervalue)
{
SqlCommand cmd = null;
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE '%#fv%'", this.conn)
cmd.Parameters.Add("#fv", SqlDbType.NVarChar);
cmd.Parameters["#fv"].Value=filtervalue;
rdr = cmd.ExecuteReader();
while(rdr.Read())
{
//Do something with the answer from the DB
}
}
But this does not do the trick. I also tried AddWithValue, but I got no luck.
When creating a stop-point on the line, where #fv should be replaced, I can go through the code line-by-line. And I can see that the command, where #fv should be replaced, is processed with no error. But #fv is not replaced (or at least I cannot see the replacement in the debug console).
What am I doing wrong?
EDIT:
thank you for your replies. Leaving out the single quotes ( ' ) did the trick.
And I also learned that this is not a string replacement. Thank you.
Just one word: The connection is not left open all the time. It's immediately closed, when it's not needed any more; and re-established when needed again - I just forgot to write that into my sample code.
Again: Thank you for your help!
You can't see it being replaced in your debug session; the replacement occurs in the SQL server code itself...
The client (your code) send both the SQL-string and the value of the parameter as seperate things to the server. There the SQL Engine 'replaces' the parameter with its value while executing it.
You should also put the 'wildcards' inside your parametervalue, not inside the query.
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE #fv ", this.conn)
cmd.Parameters.Add("#fv", SqlDbType.NVarChar);
cmd.Parameters["#fv"].Value= "%" + filtervalue + "%";
The parameter is not working because it is inside a string literal. You want to build the string like this:
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE '%' + #fv + '%'");
While we're at it, keeping a global connection like that is bad. It can cause strange side effects, especially in web apps. Instead, keep a global connection string, and then use that string to create a new connection on each request.
Also, "replace" is the wrong word here. Sql parameters are never replaced, even when they work. That's the whole point. There is no string replacement into your query at any point, ever. It's more like you declared an #fv variable at the server level in a stored procedure, and assigned your data directly to that variable. In this way, there is no possibility for a vulnerability in parameter replacement code, because the data portion of your query remains separate throughout the execution process. In same way, don't think in terms of "sanitizing" a parameter for a query; instead, think in terms of quarantining the data.

Resources