Error updating MySQL database: DUPLICATE DEFAULT ENTRY FOR PRIMARY KEY = '0' - mysql

I have a MySQL dsetup called in-out and I have written a vb.NET client program to get information from the table in the database, display it in a data grid view, and then allow to user to edit this information and update it on the server.
Right now I am hosting the server on my Gateway laptop and also connecting to it from the same laptop therefore I'm using localhost as the server name. My problem is that when I go into the program and change the information and click update, nothing happens... The information stays the same yet there is no sign of an error, syntax failure, or program crash.
I've tried running this on another computer in my house and I get the same results. I can access the information without a hitch but updating it is where I run into trouble. If there was a problem with my code it would have displayed some sort of error or asked me to debug my script, which would have made it a lot easier to solve, therefore I am certain that it has something to do with my database.
Before I got to this step, I kept getting an error when retrieving the information that said something like
DUPLICATE DEFAULT ENTRY FOR PRIMARY KEY = '0'
Which means that the columns in the table related to this error cannot have more than one default value of '0', but that's gone now... (Even though I didn't change anything)
Here is the script that will recreate my database layout. Just run it in MySQL WorkBench or MySQL Query Browser (or what ever your using to manage your SQL Database).
Here's my update code: (just in case the problem lies in my program not the database)
Private Sub cmdupdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdupdate.Click
Dim conn As New MySqlConnection
Dim myCommand As New MySqlCommand
'#######
conn.ConnectionString = "server=" & frmLogin.txtserver.Text & ";" _
& "user id=" & frmLogin.txtusername.Text & ";" _
& "password=" & frmLogin.txtpassword.Text & ";" _
& "database=in_out"
'#######
myCommand.Connection = conn
myCommand.CommandText = "INSERT INTO event(user_id, message_id, timestamp, status, creator)" _
& "VALUES(?UserID, ?MessageID, NOW(), ?Status, ?Creator)"
myCommand.Parameters.AddWithValue("?UserID", myUserID)
myCommand.Parameters.AddWithValue("?MessageID", cbomessage.SelectedValue)
myCommand.Parameters.AddWithValue("?Status", cbostatus.SelectedItem)
myCommand.Parameters.AddWithValue("?Creator", myUserID)
Try
conn.Open()
myCommand.ExecuteNonQuery()
Catch myerror As MySqlException
MsgBox("There was an error updating the database: " & myerror.Message)
End Try
refreshStatus(dgvstatus)
End Sub
Additional Details:
OS: Windows 7 Professional x64
Software: Visual Basic 2010 Express
Server Name: 'Localhost'
SQL Manager: MySQL Workbench 5.2.34 CE

Seems that you have some sort of transaction problem going on...
try to add myCommand.Connection.Close(); after the ExecuteNonQuery()
EDIT - as per comment:
Some links to learn SQL:
http://www.w3schools.com/sql/default.asp
http://www.sqlcourse.com/index.html
http://www.sql-tutorial.net/
http://www.mysqltutorial.org/
EDIT 2:
UPDATE event SET
timestamp = NOW(),
status = ?Status
WHERE user_id = ?UserID AND message_id = ?MessageID AND creator = ?Creator;
Since there is not enough details about the data model the above UPDATE statement assumes that the columns user_id and message_id and creator together identify a row uniquely... and update the timestamp and status columns accordingly...

Related

Microsoft Access Query '' is Corrupt [duplicate]

Since installing the windows update for Office 2010 resolving KB 4484127 I get an error while executing queries which contain a WHERE clause.
For example executing this query:
DoCmd.RunSQL "update users set uname= 'bob' where usercode=1"
Results in this error:
Error number = 3340 Query ' ' is corrupt
The update in question is currently still installed:
How can I successfully run my queries? Should I just uninstall this update?
Summary
This is a known bug caused by the Office updates released on November 12, 2019. The bug affects all versions of Access currently supported by Microsoft (from Access 2010 to 365).
This bug has been fixed.
If you use a C2R (Click-to-Run) version of Office, use "Update now":
Access 2010 C2R: Fixed in Build 7243.5000
Access 2013 C2R: Fixed in Build 5197.1000
Access 2016 C2R: Fixed in Build 12130.20390
Access 2019 (v1910): Fixed in Build 12130.20390
Access 2019 (Volume License): Fixed in Build 10353.20037
Office 365 Monthly Channel: Fixed in Build 12130.20390
Office 365 Semi-Annual: Fixed in Build 11328.20480
Office 365 Semi-Annual Extended: Fixed in Build 10730.20422
Office 365 Semi-Annual Targeted: Fixed in Build 11929.20494
If you use an MSI version of Office, install the update matching your Office version. All of these patches have been released on Microsoft Update, so installing all pending Windows Updates should suffice:
Access 2010 MSI: Fixed in KB4484193
Access 2013 MSI: Fixed in KB4484186
Access 2016 MSI: Fixed in KB4484180
Example
Here is a minimal repro example:
Create a new Access database.
Create a new, empty table "Table1" with the default ID field and a Long Integer field "myint".
Execute the following code in the VBA editor's Immediate Window:
CurrentDb.Execute "UPDATE Table1 SET myint = 1 WHERE myint = 1"
Expected result: The statement successfully finishes.
Actual result with one of the buggy updates installed: Run-time error 3340 occurs ("Query '' is corrupt").
Related links:
MSDN forum thread
Official Microsoft page for this bug
Simplest Solution
For my users, waiting nearly a month till December 10 for a fix release from Microsoft is not an option. Nor is uninstalling the offending Microsoft update across several government locked down workstations.
I need to apply a workaround, but am not exactly thrilled with what Microsoft suggested - creating and substituting a query for each table.
The solution is to replace the Table name with a simple (SELECT * FROM Table) query directly in the UPDATE command. This does not require creating and saving a ton of additional queries, tables, or functions.
EXAMPLE:
Before:
UPDATE Table1 SET Field1 = "x" WHERE (Field2=1);
After:
UPDATE (SELECT * FROM Table1) SET Field1 = "x" WHERE (Field2=1);
That should be much easier to implement across several databases and applications (and later rollback).
This is not a Windows update problem, but a problem that was introduced with the November Patch Tuesday Office release. A change to fix a security vulnerability causes some legitimate queries to be reported as corrupt.
Because the change was a security fix, it impacts ALL builds of Office, including 2010, 2013, 2016, 2019, and O365.
The bug has been fixed in all channels, but the timing of delivery will depend on what channel you are on.
For 2010, 2013, and 2016 MSI, and 2019 Volume License builds, and the O365 Semi-annual channel, the fix will be in the December Patch Tuesday build, Dec 10.
For O365, Monthly Channel, and Insiders, this will be fixed when the October fork is released, currently planned for Nov 24.
For the Semi-Annual channel, the bug was introduced in 11328.20468, which was released Nov 12, but doesn’t roll out to everyone all at once.
If you can, you might want to hold off on updating until Dec 10.
The issue occurs for update queries against a single table with a criteria specified (so other types of queries shouldn’t be impacted, nor any query that updates all rows of a table, nor a query that updates the result set of another query).
Given that, the simplest workaround in most cases is to change the update query to update another query that selects everything from the table, rather than updating the query directly.
I.e., if you have a query like:
UPDATE Table1 SET Table1.Field1 = "x" WHERE ([Table1].[Field2]=1);
Then, create a new query (Query1) defined as:
Select * from Table1;
and update your original query to:
UPDATE Query1 SET Query1.Field1 = "x" WHERE ([Query1].[Field2]=1);
Official page: Access error: "Query is corrupt"
To temporarily resolve this issue depends on the Access version in use:
Access 2010 Uninstall update KB4484127
Access 2013 Uninstall update KB4484119
Access 2016 Uninstall update KB4484113
Access 2019 IF REQUIRED (tbc). Downgrade from Version 1808 (Build 10352.20042) to Version 1808 (Build 10351.20054)
Office 365 ProPlus Downgrade from Version 1910 (Build 12130.20344) to a previous build, see https://support.microsoft.com/en-gb/help/2770432/how-to-revert-to-an-earlier-version-of-office-2013-or-office-2016-clic
We and our clients have struggled with this the last two days and finally wrote a paper to discuss the issue in detail along with some solutions: http://fmsinc.com/MicrosoftAccess/Errors/query_is_corrupt/
It includes our findings that it impacts Access solutions when running update queries on local tables, linked Access tables, and even linked SQL Server tables.
It also impacts non-Microsoft Access solutions using the Access Database Engine (ACE) to connect to Access databases using ADO. That includes Visual Studio (WinForm) apps, VB6 apps, and even web sites that update Access databases on machines that never had Access or Office installed on them.
This crash can even impact Microsoft apps that use ACE such as PowerBI, Power Query, SSMA, etc. (not confirmed), and of course, other programs such as Excel, PowerPoint or Word using VBA to modify Access databases.
In addition to the obvious uninstallation of the offending Security Updates, we also include some options when it's not possible to uninstall due to permissions or distribution of Access applications to external customers whose PCs are beyond your control. That includes changing all the Update queries and distributing the Access applications using Access 2007 (retail or runtime) since that version isn't impacted by the security updates.
Use the following module to automatically implement Microsofts suggested workaround (using a query instead of a table). As a precaution, backup your database first.
Use AddWorkaroundForCorruptedQueryIssue() to add the workaround and RemoveWorkaroundForCorruptedQueryIssue() to remove it at any time.
Option Compare Database
Option Explicit
Private Const WorkaroundTableSuffix As String = "_Table"
Public Sub AddWorkaroundForCorruptedQueryIssue()
On Error Resume Next
With CurrentDb
Dim tableDef As tableDef
For Each tableDef In .tableDefs
Dim isSystemTable As Boolean
isSystemTable = tableDef.Attributes And dbSystemObject
If Not EndsWith(tableDef.Name, WorkaroundTableSuffix) And Not isSystemTable Then
Dim originalTableName As String
originalTableName = tableDef.Name
tableDef.Name = tableDef.Name & WorkaroundTableSuffix
Call .CreateQueryDef(originalTableName, "select * from [" & tableDef.Name & "]")
Debug.Print "OldTableName/NewQueryName" & vbTab & "[" & originalTableName & "]" & vbTab & _
"NewTableName" & vbTab & "[" & tableDef.Name & "]"
End If
Next
End With
End Sub
Public Sub RemoveWorkaroundForCorruptedQueryIssue()
On Error Resume Next
With CurrentDb
Dim tableDef As tableDef
For Each tableDef In .tableDefs
Dim isSystemTable As Boolean
isSystemTable = tableDef.Attributes And dbSystemObject
If EndsWith(tableDef.Name, WorkaroundTableSuffix) And Not isSystemTable Then
Dim originalTableName As String
originalTableName = Left(tableDef.Name, Len(tableDef.Name) - Len(WorkaroundTableSuffix))
Dim workaroundTableName As String
workaroundTableName = tableDef.Name
Call .QueryDefs.Delete(originalTableName)
tableDef.Name = originalTableName
Debug.Print "OldTableName" & vbTab & "[" & workaroundTableName & "]" & vbTab & _
"NewTableName" & vbTab & "[" & tableDef.Name & "]" & vbTab & "(Query deleted)"
End If
Next
End With
End Sub
'From https://excelrevisited.blogspot.com/2012/06/endswith.html
Private Function EndsWith(str As String, ending As String) As Boolean
Dim endingLen As Integer
endingLen = Len(ending)
EndsWith = (Right(Trim(UCase(str)), endingLen) = UCase(ending))
End Function
You can find the latest code on my GitHub repository.
AddWorkaroundForCorruptedQueryIssue() will add the suffix _Table to all non-system tables, e.g. the table IceCreams would be renamed to IceCreams_Table.
It will also create a new query using the original table name, that will select all columns of the renamed table. In our example, the query would be named IceCreams and would execute the SQL select * from [IceCreams_Table].
RemoveWorkaroundForCorruptedQueryIssue() does the reverse actions.
I tested this with all kinds of tables, including external non-MDB tables (like SQL Server). But be aware, that using a query instead of a table can lead to non-optimized queries being executed against a backend database in specific cases, especially if your original queries that used the tables are either of poor quality or very complex.
(And of course, depending on your coding style, it is also possible to break things in your application. So after verifying that the fix generally works for you, it's never a bad idea to export all your objects as text and use some find replace magic to ensure that any occurrences of table names use will be run against the queries and not the tables.)
In my case, this fix works largely without any side effects, I just needed to manually rename USysRibbons_Table back to USysRibbons, as I hadn't marked it as a system table when I created it in the past.
For those looking to automate this process via PowerShell, here are a few links I found that may be helpful:
Detect and Remove the Offending Updates
There is a PowerShell script available here https://www.arcath.net/2017/09/office-update-remover that searches the registry for a specific Office update (passed in as a kb number) and removes it using a call to msiexec.exe. This script parses out both GUIDs from the registry keys to build the command to remove the appropriate update.
One change that I would suggest would be using the /REBOOT=REALLYSUPPRESS as described in How to uninstall KB4011626 and other Office updates (Additional reference: https://learn.microsoft.com/en-us/windows/win32/msi/uninstalling-patches). The command line you are building looks like this:
msiexec /i {90160000-0011-0000-0000-0000000FF1CE} MSIPATCHREMOVE={9894BF35-19C1-4C89-A683-D40E94D08C77} /qn REBOOT=REALLYSUPPRESS
The command to run the script would look something like this:
OfficeUpdateRemover.ps1 -kb 4484127
Prevent the Updates from Installing
The recommended approach here seems to be hiding the update. Obviously this can be done manually, but there are some PowerShell scripts that can help with automation.
This link: https://www.maketecheasier.com/hide-updates-in-windows-10/ describes the process in detail, but I will summarize it here.
Install the Windows Update PowerShell Module.
Use the following command to hide an update by KB number:
Hide-WUUpdate -KBArticleID KB4484127
Hopefully this will be a help to someone else out there.
VBA-Script for MS-Workaround:
It is recommended to remove the buggy update, if possible (if not try my code), at least for the MSI Versions. See answer https://stackoverflow.com/a/58833831/9439330 .
For CTR(Click-To-Run) Versions, you have to remove all Office November-Updates, what may cause serious security issues (not sure if any critical fixes would be removed).
From #Eric's comments:
If you useTable.Tablenameto bind forms, they get unbound as the former table-name is now a query-name!.
OpenRecordSet(FormerTableNowAQuery, dbOpenTable) will fail ( as its a query now, not a table anymore)
Caution! Just quick tested against Northwind.accdb on Office 2013 x86 CTR No Warranty!
Private Sub RenameTablesAndCreateQueryDefs()
With CurrentDb
Dim tdf As DAO.TableDef
For Each tdf In .TableDefs
Dim oldName As String
oldName = tdf.Name
If Not (tdf.Attributes And dbSystemObject) Then 'credit to #lauxjpn for better check for system-tables
Dim AllFields As String
AllFields = vbNullString
Dim fld As DAO.Field
For Each fld In tdf.Fields
AllFields = AllFields & "[" & fld.Name & "], "
Next fld
AllFields = Left(AllFields, Len(AllFields) - 2)
Dim newName As String
newName = oldName
On Error Resume Next
Do
Err.Clear
newName = newName & "_"
tdf.Name = newName
Loop While Err.Number = 3012
On Error GoTo 0
Dim qdf As DAO.QueryDef
Set qdf = .CreateQueryDef(oldName)
qdf.SQL = "SELECT " & AllFields & " FROM [" & newName & "]"
End If
Next
.TableDefs.Refresh
End With
End Sub
For testing:
Private Sub TestError()
With CurrentDb
.Execute "Update customers Set City = 'a' Where 1=1", dbFailOnError 'works
.Execute "Update customers_ Set City = 'b' Where 1=1", dbFailOnError 'fails
End With
End Sub
I replaced the currentDb.Execute and Docmd.RunSQL with a helper function. That can pre-process and change the SQL Statement if any update statement contains only one table. I already have a dual(single row, single column) table so i went with a fakeTable option.
Note: This won't change your query objects. It will only help SQL executions via VBA. If you would like to change your query objects, use FnQueryReplaceSingleTableUpdateStatements and update your sql in each of your querydefs. Shouldn't be a problem either.
This is just a concept (If it's a single table update modify the sql before execution). Adapt it as per your needs. This method does not create replacement queries for each table (which may be the easiest way but has it's own drawbacks. i.e performance issues)
+Points:
You can continue to use this helper even after MS fixing the bug it won't change anything. In case, future brings another problem, you are ready to pre-process your SQL in one place. I didn't go for uninstalling updates method because that requires Admin access + gonna take too long to get everyone on the correct version + even if you uninstall, some end users's group policy installs the latest update again. You are back to the same problem.
If you have access to the source-code, use this method and you are 100% sure that no enduser is having the issue.
Public Function Execute(Query As String, Optional Options As Variant)
'Direct replacement for currentDb.Execute
If IsBlank(Query) Then Exit Function
'invalid db options remove
If Not IsMissing(Options) Then
If (Options = True) Then
'DoCmd RunSql query,True ' True should fail so transactions can be reverted
'We are only doing this so DoCmd.RunSQL query, true can be directly replaced by helper.Execute query, true.
Options = dbFailOnError
End If
End If
'Preprocessing the sql command to remove single table updates
Query = FnQueryReplaceSingleTableUpdateStatements(Query)
'Execute the command
If ((Not IsMissing(Options)) And (CLng(Options) > 0)) Then
currentDb.Execute Query, Options
Else
currentDb.Execute Query
End If
End Function
Public Function FnQueryReplaceSingleTableUpdateStatements(Query As String) As String
' ON November 2019 Microsoft released a buggy security update that affected single table updates.
'https://stackoverflow.com/questions/58832269/getting-error-3340-query-is-corrupt-while-executing-queries-docmd-runsql
Dim singleTableUpdate As String
Dim tableName As String
Const updateWord As String = "update"
Const setWord As String = "set"
If IsBlank(Query) Then Exit Function
'Find the update statement between UPDATE ... SET
singleTableUpdate = FnQueryContainsSingleTableUpdate(Query)
'do we have any match? if any match found, that needs to be preprocessed
If Not (IsBlank(singleTableUpdate)) Then
'Remove UPDATe keyword
If (VBA.Left(singleTableUpdate, Len(updateWord)) = updateWord) Then
tableName = VBA.Right(singleTableUpdate, Len(singleTableUpdate) - Len(updateWord))
End If
'Remove SET keyword
If (VBA.Right(tableName, Len(setWord)) = setWord) Then
tableName = VBA.Left(tableName, Len(tableName) - Len(setWord))
End If
'Decide which method you want to go for. SingleRow table or Select?
'I'm going with a fake/dual table.
'If you are going with update (select * from T) as T, make sure table aliases are correctly assigned.
tableName = gDll.sFormat("UPDATE {0},{1} SET ", tableName, ModTableNames.FakeTableName)
'replace the query with the new statement
Query = vba.Replace(Query, singleTableUpdate, tableName, compare:=vbDatabaseCompare, Count:=1)
End If
FnQueryReplaceSingleTableUpdateStatements = Query
End Function
Public Function FnQueryContainsSingleTableUpdate(Query As String) As String
'Returns the update ... SET statment if it contains only one table.
FnQueryContainsSingleTableUpdate = ""
If IsBlank(Query) Then Exit Function
Dim pattern As String
Dim firstMatch As String
'Get the pattern from your settings repository or hardcode it.
pattern = "(update)+(\w|\s(?!join))*set"
FnQueryContainsSingleTableUpdate = FN_REGEX_GET_FIRST_MATCH(Query, pattern, isGlobal:=True, isMultiline:=True, doIgnoreCase:=True)
End Function
Public Function FN_REGEX_GET_FIRST_MATCH(iText As String, iPattern As String, Optional isGlobal As Boolean = True, Optional isMultiline As Boolean = True, Optional doIgnoreCase As Boolean = True) As String
'Returns first match or ""
If IsBlank(iText) Then Exit Function
If IsBlank(iPattern) Then Exit Function
Dim objRegex As Object
Dim allMatches As Variant
Dim I As Long
FN_REGEX_GET_FIRST_MATCH = ""
On Error GoTo FN_REGEX_GET_FIRST_MATCH_Error
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Multiline = isMultiline
.Global = isGlobal
.IgnoreCase = doIgnoreCase
.pattern = iPattern
If .test(iText) Then
Set allMatches = .Execute(iText)
If allMatches.Count > 0 Then
FN_REGEX_GET_FIRST_MATCH = allMatches.item(0)
End If
End If
End With
Set objRegex = Nothing
On Error GoTo 0
Exit Function
FN_REGEX_GET_FIRST_MATCH_Error:
FN_REGEX_GET_FIRST_MATCH = ""
End Function
Now just CTRL+F
Search and replace docmd.RunSQL with helper.Execute
Search and replace [currentdb|dbengine|or your dbobject].execute with helper.execute
have fun!
Ok I'll chime in here as well, because even though this bug has been fixed, that fix has yet to populate fully through various enterprises where the end users may not be able to update (like my employer...)
Here's my workaround for DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1". Just comment out the offending query and drop in the code below.
'DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1"
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("users")
rst.MoveLast
rst.MoveFirst
rst.FindFirst "[usercode] = 1" 'note: if field is text, use "[usercode] = '1'"
rst.Edit
rst![uname] = "bob"
rst.Update
rst.Close
Set rst = Nothing
I can't say it's pretty, but it gets the job done.

update computer from mysql server

I have been working on this for hours I am sure it is something simple I am missing. I have a remote server that gets updated form from another pc once the sever gets updated I would like my PC to get the changes from the server I cant seem to get it done the closest I have been is shown below any help would be greatly appreciated. Connection string no issue just trying to update the PC database to Match the server database
Sub updateRecord()
Try
Dim dbConnection As New MySqlConnection("server=" & My.Settings.ServerIPAddress & ";user id=root;password=T#ttleT#le102;persistsecurityinfo=True;database=pmdatabase")
dbConnection.Open()
Using Adapter As New MySqlDataAdapter("Select * From tblpmdata", dbConnection)
Using Table As New DataTable
Adapter.Fill(Table) 'test to see if table is correct
Adapter.Fill(Me.PmdatabaseDataSet.tblpmdata)
Me.PmdatabaseDataSet.AcceptChanges()
Me.TableAdapterManager.UpdateAll(Me.PmdatabaseDataSet)
MsgBox(Table.Rows.Count)only shows 5 records when the server has 8 records
End Using
End Using
dbConnection.Close()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub

VB Application Search MySQL Database for Login Credentials

Okay, I posted this question previously but only got half of the answer I require. To put simply I have an application which checks the log-in credentials with those stored in the My SQL Database. The awkward problem I have hear is that I've created it in the way that best suits me, and in a way that's simple for me to understand -- I'm not experienced with programming and self taught. The method I used - though prone to 'injection attacks' and other such dangers is suitable for my application and there's obviously security in my application (CD-Key checks & passwords.)
So, the actual details:
The connection is established successfully and I'm able to manipulate the data from the application.
Connection Code:
Public Sub conecDB()
'This is called upon program start.
Dim connectionString As String = SQLLoginIn.serveriptxt.Text 'This is the server IP/Server name. If server is intalled on your local machine, your IP should be 127.0.0.1 or you may use localhost
Dim strDbase As String = SQLLoginIn.databasenametxt.Text 'Database name
Dim strUser As String = SQLLoginIn.databaseusertxt.Text 'Database user
Dim strPass As String = "CENSORED" 'Database password
If connDB.State <> ConnectionState.Open Then connDB.ConnectionString = "Data Source=" & connectionString.Trim & ";Initial Catalog=" & strDbase.Trim & ";MultipleActiveResultSets=False;User ID=" & strUser.Trim & ";Password=" & strPass
If connDB.State <> ConnectionState.Open Then connDB.Open()
End Sub
That works great, for what I need it to do.
I add the records (of a separate table) to a listview in my program called lvRec
Code:
Public Sub dispRec(ByVal PstrSQL As String)
'This is called once the connection is established.
frmMain.lvRec.Items.Clear()
With comDB
.CommandText = PstrSQL
rdDB = .ExecuteReader
End With
Do While rdDB.Read
Item = frmMain.lvRec.Items.Add(rdDB!Members_ID.ToString)
Item.SubItems.Add(rdDB!Gamer_Tag.ToString.Trim)
'And a bunch of more
Item.SubItems.Add(rdDB!Games_Owned.ToString.Trim)
My.Application.DoEvents()
Loop
rdDB.Close()
End Sub
So now that you've seen how I'm working with SQL you're probably screaming "That's not how you do it!", but as I sayed, it works for me :) The problem occurs when I want to varifiy the user with their own Username & Password stored on a different Table called Logins.
I know I need to/can use the below code to query the users input with the log-in credentials in the Logins table, but I don't know how to return anything use-able like a Boolean result or something similar. I simply want to know if the credentials teh user entered are correct, so a TRUE for it existing would be great.
The SQL Query:
SQL = "Select * from logins " & _
"where Name like '%" & Me.Usernametxt.Text.Trim & _
"%' AND Passkey LIKE '" & Me.Passwordtxt.Text.Trim & "'"
I know this is a lot of detail for such a simple question but I needed people to understand the way I've went around this. Any help would be much appreciated.
Not an answer but to help you get started with authentication in a more robust fashion...
[Please bear in mind that you should never take an SO answer as gospel as all of us make mistakes and security is a very difficult topic to do right even for the professionals...]
The process you want to follow is something like the following (in pseudocode)
UserRecords = ExecuteSql("SELECT UserID, PasswordHash, Salt, IsEnabled FROM Users WHERE Username = '<Blah>'")
If UserRecords.Count <> 1
'Login Failed
End If
Dim UserRecord = UserRecords.First
If Hash(PasswordEnteredByUser & UserRecord.Salt) <> UserRecord.PasswordHash
'Login Failed
End If
If UserRecord.IsEnabled <> True
'Login Failed
End If
' Login was successful, continue as planned
A hashing function works by taking an input eg MyPassword! and converting it to something else eg AB0653AAAF330. Think of it as a sausage maker - you put a pig in one end and get a sausage out the other end. It's impossible to turn a sausage back into a pig. In theory, however, putting the same pig in twice will produce the same sausage. By storing the sausage, not the pig a database breach won't reveal users passwords
The "Salt" comes in to play when your database is breached. Imagine two users picked the same password. You know bob's password is MyPassword! and you can see it's stored as AB0653AAAF330. Anyone else with a password hash of AB0653AAAF330 has the same password - meaning multiple accounts could be breached at once.
If, on the other hand, there is a random string which is user-specific and appended to their password pre-hashing, Bob's password is treated as MyPassword!AAAAAAAAA which (say) hashes to 099DEF3333445 and Jane's (identical) password is treated as MyPassword!BBBBBBBBBwhich (say) hashes to AAAAE1234400.
In short, knowing Bob's password won't tell you that Jane has the same one.
This is still a hideously incomplete example. There are many other things to consider (eg the hashing algorithm. MD5 hashing is considered weak, bcrypt is intentionally computationally expensive to make brute-forcing more difficult, etc) but hopefully it will give you a rough idea to get you started.
You also want to make authentication binary - success or failure. If you tell the user "That username is invalid" (as opposed to a simple "Login Failed") as a help to them, they can use the feature to determine valid usernames by repetition.
I wholeheartedly recommend you read this article on how a company was compromised via SQL injection - once you know how it works in detail, you'll be able to see the pitfalls...
Another feature to consider is a failed login "window" - a sliding period of time in which a certain number of failed logins is allowed before that account becomes locked (possibly temporarily). This means brute-forcing passwords becomes impractical. This is usually handled by recording a FailedLoginAttempts and a FailedLoginTimeout against the user account. If a login fails, set the Timeout to now + 30 mins. If attempts > some threshold and the timeout hasn't expired, lock the account. If the login fails, extend the timeout. You get the idea.
Finally, if you're really security-conscious, when a user logs in successfully, tell them when they last logged in. If someone's been on holiday for a week and sees "Your last login was yesterday" they know instantly that something's wrong.
All the best.
On the general command on how to return a singular results from a SQL command:
Dim sqlConnection1 As New SqlConnection("Your Connection String")
Dim cmd As New SqlCommand
Dim success As Object
cmd.CommandText = "SELECT COUNT(*) FROM logins " & _
"WHERE Name = #Name " & _
"AND Passkey = #Passkey"
cmd.Parameters.AddWithValue("#Name",Me.Usernametxt.Text.Trim)
cmd.Parameters.AddWithValue("#Passkey",Me.Passwordtxt.Text.Trim)
cmd.CommandType = CommandType.Text
cmd.Connection = sqlConnection1
sqlConnection1.Open()
success = cmd.ExecuteScalar()
sqlConnection1.Close()

Microsoft SQL Server 2008 - Error 1206 - The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction

I'm running a Pivotal application (5.9) and have a server process which always crashes (MS SQL error 1206).
I have a Pivotal "active form" with an update button, which, when clicked, calls a server process (VB 6 custom DLL)
This custom DLL :
retrieves the list of all users who can use the Pivotal application ("users" standard table) by using the Pivotal API, and then creates its own connection to another database using a connection string)
For each users, it constructs and run the a query :
strSql = select SUM(stoResourceSize) totalResourceSize from " & dbName &
".dbo.Resource Where StorageID = 3 and XptDestinationAddress like "
strSql = strSql & "'%" & userName & "%'"
The connection string used to run the query is the following :
strConnect = "Driver={SQL Server};" & _
"Server=" & SatteliteName & ";" & _
"UID=" & SyncStreamDBName & ";" & _
"Trusted_Connection=Yes"
It works fine but the query crashes after about 80 users processed, with error 1206 and SQLState = 37000
I can reproduce that problem every single time. At a given time, this query will crash.
I have added a lot of trace and the last query works fine if I run it on the server "by hand".
It doesn't crash on the same user every time, it looks like it is random. Besides, in the application there exists another server procedure which can run the same query but only for one user. That one will run fine and never crash.
I have also tried to enabled ODBC trace but it wouldn't give me any useful information.
I'm pretty confidnet it is a connection or configuration problem but I don't know what.
This problem is reproducible both in developpment and testing environnments.
edit: actually it seems that my server process can crash at various points, not necessarily when executing that request.
It seems to crash when I open ADODB recordsets (of course, before touching any recordset, I verify that both EOF and BOF are false, and they are).

Programatically link SQL Server tables in a new Access 2007 database

I manage an SQL Server 2005 Database and I would like to give read-only access to the necessary tables to a group of 20-30 networked users who are able to use the GUI in MS Access 2007 to write or modify their own queries to the database, with some help.
I would like to distribute an Access database with a single form that would create links to the necessary tables. All of these users are included in a group with read-only permissions to the SQL Server database. I can distribute a dsn file for the connection, but I haven't found a way to programatically create the links to the 50 or so tables they might need, with their network credentials from an otherwise empty Access database.
I found a line of VB code from answer to a similar question onstackoverflow (below), but I was wondering if there was any simpler way than running the modified command once for each of the 50 or so tables.
DoCmd.TransferDatabase acLink, "ODBC Database", "ODBC;DRIVER=Microsoft ODBC for Oracle;SERVER=myserver;UID=myuser;PWD=mypassword", acTable, "SCHEMA.TABLE", "TABLE", False, True
I just wrote an article last week detailing a way to quickly link all tables in an SQL Database to Access. Here are some Access methods that will help. Read the article for more instructions on using it.
You will need to reference the Microsoft ActiveX Data Objects library.
Sub LinkAllTables(Server As String, database As String, OverwriteIfExists As Boolean)
'Usage Example (link all tables in database "SQLDB" on SQL Server Instance SQO01, overwriting any existing linked tables.
'linkalltables "SQL01","SQLDB", true
'This will also update the link if the underlying table definition has been modified.
Dim rsTableList As New ADODB.Recordset
Dim sqlTableList As String
sqlTableList = "SELECT [name] as tablename FROM sysObjects WHERE (type = 'U')"
rsTableList.Open sqlTableList, BuildSQLConnectionString(Server, database)
While Not rsTableList.EOF
If LinkTable(rsTableList("tableName"), Server, database, rsTableList("tableName"), OverwriteIfExists) Then
Debug.Print "Linked: " & rsTableList("tableName")
End If
rsTableList.MoveNext
Wend
rsTableList.Close
Debug.Print "Done."
End Sub
Function LinkTable(LinkedTableAlias As String, Server As String, database As String, SourceTableName As String, OverwriteIfExists As Boolean)
'This method will also update the link if the underlying table definition has been modified.
'The overwrite parameter will cause it to re-map/refresh the link for LinktedTable Alias, but only if it was already a linked table.
' it will not overwrite an existing query or local table with the name specified in LinkedTableAlias.
'Links to a SQL Server table without the need to set up a DSN in the ODBC Console.
Dim dbsCurrent As database
Dim tdfLinked As TableDef
' Open a database to which a linked table can be appended.
Set dbsCurrent = CurrentDb()
'Check for and deal with the scenario ofthe table alias already existing
If TableNameInUse(LinkedTableAlias) Then
If (Not OverwriteIfExists) Then
Debug.Print "Can't use name '" + LinkedTableAlias + "' because it would overwrite existing table."
Exit Function
End If
'delete existing table, but only if it is a linked table
If IsLinkedTable(LinkedTableAlias) Then
dbsCurrent.TableDefs.Delete LinkedTableAlias
dbsCurrent.TableDefs.Refresh
Else
Debug.Print "Can't use name '" + LinkedTableAlias + "' because it would overwrite an existing query or local table."
Exit Function
End If
End If
'Create a linked table
Set tdfLinked = dbsCurrent.CreateTableDef(LinkedTableAlias)
tdfLinked.SourceTableName = SourceTableName
tdfLinked.Connect = "ODBC;DRIVER={SQL Server};SERVER=" & Server & ";DATABASE=" & database & ";TRUSTED_CONNECTION=yes;"
On Error Resume Next
dbsCurrent.TableDefs.Append tdfLinked
If (Err.Number = 3626) Then 'too many indexes on source table for Access
Err.Clear
On Error GoTo 0
If LinkTable(LinkedTableAlias, Server, database, "vw" & SourceTableName, OverwriteIfExists) Then
Debug.Print "Can't link directly to table '" + SourceTableName + "' because it contains too many indexes for Access to handle. Linked to view '" & "vw" & SourceTableName & "' instead."
LinkTable = True
Else
Debug.Print "Can't link table '" + SourceTableName + "' because it contains too many indexes for Access to handle. Create a view named '" & "vw" & SourceTableName & "' that selects all rows/columns from '" & SourceTableName & "' and try again to circumvent this."
LinkTable = False
End If
Exit Function
End If
On Error GoTo 0
tdfLinked.RefreshLink
LinkTable = True
End Function
Function BuildSQLConnectionString(Server As String, DBName As String) As String
BuildSQLConnectionString = "Driver={SQL Server};Server=" & Server & ";Database=" & DBName & ";TRUSTED_CONNECTION=yes;"
End Function
Function TableNameInUse(TableName As String) As Boolean
'check for local tables, linked tables and queries (they all share the same namespace)
TableNameInUse = DCount("*", "MSYSObjects", "(Type = 4 or type=1 or type=5) AND [Name]='" & TableName & "'") > 0
End Function
Function IsLinkedTable(TableName As String) As Boolean
IsLinkedTable = DCount("*", "MSYSObjects", "(Type = 4) AND [Name]='" & TableName & "'") > 0
End Function
In addition to what David proposed, you could have a local (client side) table listing the list of tables available through the SQL connection. You could then write a piece of VBA code that will browse this table to establish all corresponding connections:
Dim rsTable as DAO.recordset
set rsTable = currentDb.openRecordset("Tbl_Tables")
if rsTable.EOF and rsTable.BOF then
else
rsTable.moveFirst
Do while not rsTable.EOF
DoCmd.openDatabase .... 'enumerate here all needed paarmeters with rsTable.fields("tableName") in the string'
rsTable.moveNext
Loop
Endif
rsTable.close
set rsTable = Nothing
This piece of code was written on the fly, so I cannot garantee it will work 'as is'. This code could for example be launched at startup (through the autoexec macro) so that your users will have their links ready when they open their app.
The 'view-only' thing can be easily managed by listing corresponding users (or, if you have a Domain, the corresponding group of users) as 'data readers' on your SQL server.
Is there a special reason why you want to re-create the links every time?
It would be much simpler to create once the mdb with the linked tables, and distribute that mdb to your users.
You might also want to link SQL Server Views (instead of tables) to Access tables, in order to make sure it's read only, maybe pre-join some tables, and eliminate some fields they do not require.
Why not use an Active Data Project in Access?
Linked tables are really only useful if you also need local (unlinked) tables. If you can keep all the tables and views on SQL Server and leave the forms in Access, an ADP will work fine and won't require "linking" any tables manually or via scripting.
In response to Patrick below, if you don't want them mucking around creating queries in your real SQL Server store, create a second SQL Server database that they have rights to create and update queries in, and create VIEWs like the following:
CREATE VIEW mytable AS SELECT * FROM [real database].dbo.mytable
Thus, when you change your master data tables, you only have to make a change to the VIEW in their shared SQL Server database, not a change to each Access database.
Side advantage #1: the users can see each other's queries, thus giving a social aspect of easily sharing good queries.
Side advantage #2: since they are all in one place, you can use SQL Server to detect which user queries will break if you make a change to one of the read-only tables (by searching the view definitions created by Access).
If your SQL Server uses Windows security instead of SQL Server security, then you don't have to supply a username/password in your connect string.
Here's the standard way to do this:
on your development machine, create a DSN for your SQL Server database.
use FILE | GET EXTERNAL DATA | LINK TABLES to link to the tables via ODBC.
get Doug Steele's code to convert to DSN-less connect strings.
then just distribute the front end as is to your users.
The key to this scenario is using Windows security rather than SQL Server security -- Access silently passes the credentials when it requests the connection via ODBC. This is one reason I'd never use SQL Server security -- too much trouble!