Need to Identify the Database name in an ODBC DSN connected application - sql

I have a Delphi 6 application that uses an ODBC DSN to connect to target databases. I want to include text that lists the name of the Database the DSN is connected to. I tried using the SQL command db_name() but only received a nil in response despite having it work when I log into the SQL server.
Is there a way within Delphi to identify which Database I'm connected to? I can pull up the sys.databases table, but am not certain how to identify which database is the one I'm connected to
As an Example:
if I am connecting to the dsn LocalDSN I want to be able to display to the user that they are connected to Database, where database is the name of the sql database they are communicating with.

The ODBC DSN is stored in the Windows Registry. Keep in mind that the Windows Registry, and therefore the ODBC DSN settings, are separated between 32 and 64 bit versions. You can access this information through HKEY_LOCAL_MACHINE\Software\ODBC\ODBC.INI\[YOUR_DSN_NAME] and then read the value Database or Server to know the database or server name.
You can read the server and database name with these functions:
uses
Registry;
function ServerOfDSN(const Name: String): String;
var
R: TRegistry;
K: String;
begin
K:= 'Software\ODBC\ODBC.INI\'+Name;
R:= TRegistry.Create(KEY_READ);
try
R.RootKey:= HKEY_LOCAL_MACHINE;
if R.KeyExists(K) then begin
if R.OpenKey(K, False) then begin
if R.ValueExists('Server') then
Result:= R.ReadString('Server');
R.CloseKey;
end;
end;
finally
R.Free;
end;
end;
function DatabaseOfDSN(const Name: String): String;
var
  R: TRegistry;
  K: String;
begin
K:= 'Software\ODBC\ODBC.INI\'+Name;
R:= TRegistry.Create(KEY_READ);
try
R.RootKey:= HKEY_LOCAL_MACHINE;
if R.KeyExists(K) then begin
if R.OpenKey(K, False) then begin
if R.ValueExists('Database') then
Result:= R.ReadString('Database');
R.CloseKey;
end;
end;
finally
R.Free;
end;
end;
Depending on what database engine and drivers you're using, the contents of this registry key may be different, and therefore there's a possibility that Server or Database might not be the registry value you need, but inspect it yourself and find your value names in the registry to know how to read it.

You can use SQLGetPrivateProfileString ODBC API to get the contents of DSN created.
int SQLGetPrivateProfileString(
LPCSTR lpszSection,
LPCSTR lpszEntry,
LPCSTR lpszDefault,
LPCSTR RetBuffer,
INT cbRetBuffer,
LPCSTR lpszFilename);
Here,
lpszSection = registry section you want details for. it will be DSN name in your case.
lpszEntry = key which you want to extract value from. you want to get database name information so you need to check registry entry HKEY_LOCAL_MACHINE\Software\ODBC\ODBC.INI[YOUR_DSN_NAME] to know what is the key name to store database name information. This is because different driver can have different key name to store database name.
lpszDefault = Default value for the key specified in last argument(lpszEntry) if key is not found.
RetBuffer = Pointer to Output buffer in which value for the specified key is received.
cbRetBuffer = size of buffer pointed to by RetBuffer in characters.
lpszFilename = File name where you search these entries in. It will be odbc.ini in your case.
Sample example
CHAR *dsn_name = "Your DSN name";
CHAR db_name[20];
char *odbcini = NULL;
odbcini = "odbc.ini";
SQLGetPrivateProfileString(dsn_name, (CHAR*)"DATABASE", (CHAR*)"", db_name,
sizeof(db_name), odbcini);
It will search registry entry HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE or both depending on the config mode set(It can be set using SQLSetConfigMode ODBC API). If mode is not explicitly set, it will search both HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE.
Please refer https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetprivateprofilestring-function for more information.

Related

How to Connect to SQL from R Studio

I use Microsoft SQL Server Management Studio on Windows 10 to connect to the following database and this is what the login screen looks like:
Server Type: Database Engine
Server Name: sqlmiprod.b298745190e.database.windows.net
Authentication: SQL Server Authentication
Login: my_user_id
Password: my_password
This recent R Studio article offers an easy way to connect to SQL Servers from R Studio using the following:
con <- DBI::dbConnect(odbc::odbc(),
Driver = "[your driver's name]",
Server = "[your server's path]",
Database = "[your database's name]",
UID = rstudioapi::askForPassword("Database user"),
PWD = rstudioapi::askForPassword("Database password"),
Port = 1433)
I have two questions
What should I use as "[your driver's name]"?
What should I use as "[your database's name]"?
The server path I'll use is sqlmiprod.b298745190e.database.windows.net (from above) and I'll leave the port at 1433. If that's wrong please let me know.
Driver
From #Zaynul's comment and my own experience, the driver field is a text string with the name of the ODBC driver. This answer contains more details on this.
You probably want someting like:
Driver = 'ODBC Driver 17 for SQL Server' (from #Zaynul's comment)
Driver = 'ODBC Driver 11 for SQL Server' (from my own context)
Database
The default database you want to connect to. Roughly equivalent to starting an SQL script with
USE my_database
GO
If all your work will be within a single database then puts its name here.
In some contexts you should be able to leave this blank, but you then have to use the in_schema command to add the database name every time you connect to a table.
If you are working across multiple databases, I recommend putting the name of one database in, and then using the in_schema command to specify the database at every point of connection.
Example using the in_schema command (more details):
df = tbl(con, from = in_schema('database.schema', 'table'))
Though I have not tried it, if you do not have a schema then
df = tbl(con, from = in_schema('database', 'table'))
Should also work (I've been using this hack without issue for a while).

Linked SQL table in Access 2003 (!) not updatable

I'm working in a legacy app for the moment, upgrading Access 2003 to link to SQL Server tables (2008 R2 or later). With tables linked by code, I can insert, but not update or delete. I've tried everything on the web, no dice. Details below.
Being terse so not tl;dr.
Tables first created using upsizing wizard. In use, app has to connect to different ones in same schema, so can't just set and forget. Can't do local DSN's, many installs, though DSN file is possible. But problems there too, DSN not found. Details later.
Before the rest: Soon I'm further updating this app to Access 2016 or so. If this is different enough / easier there, I'll wait a few days. Maybe someone could suggest the best refsite for that.
* problem details follow *
Using a DSN and the UI to link a table, I get an editable table. Hurray.
But when I use the code below (found on every refsite), link is made but only selecting and inserting work. Everything else fails fails fails, no matter what.
Public Function LinkToSqlTable(sqlInstance As String, sqlDb As String,
sqlTableName As String, localTableName As String)
Dim linked As New TableDef
' ***factored-out functionality, known to work: reader can ignore*** '
DeleteTable localTableName
' connection-string steps, placeholders replaced by args '
Dim sCnx As String
sCnx = "ODBC;Driver=SQL Server;Server=_instance_;" & _
"Database=_db_;Integrated Security=SSPI"
sCnx = Replace(sCnx, "_instance_", sqlInstance)
sCnx = Replace(sCnx, "_db_", sqlDb)
' linked-table steps '
Set linked = CurrentDb.CreateTableDef(localTableName)
linked.Connect = sCnx
linked.SourceTableName = sqlTableName
CurrentDb.TableDefs.Append linked
' ui '
RefreshDatabaseWindow
End Function
* ID column or permissions? *
I thought the problem was lack of identity column originally, I added one, but no change. At least now I have a PK field like I should. ;-)
When I manually link table, UI demands to know the ID column. So could it still be it? Fine, but how do I set that in code? Searches revealed nothing.
I assume then it's permissions as sites etc. say. I also took all the steps I could think of to fix that. No dice.
* things I've tried *
Aside from the ID-column stuff I said before, these things (not in order):
Since DSN saved as a file, tried using it as exampled, in cnx string. Fail.
Used DSN contents, carefully winnowed & translated, in cnx string. Fail.
Used connection string from the table that I had connected manually with DSN. Fail.
Changed driver in cnx string across all major options, even omitted it. Fail.
Changed security in cnx to Integrated Security=SSPI and other options, and omitted entirely. Fail.
I added my actual local user as exampled, with and without password. Fail.
(Previous few options tried across earlier options, though not 100% coverage.)
In SQL Server, using SSMS, I tried security power:
Added SQS-authentication login to the instance
Matching user to the default db seen here
Gave that login-user read and write permissions in db here (plus others, sometimes)
Added matching id & pw to the cnx string. Fail.
I tried setting up this db in SQS to have let-everyone-do-everything "security" temporarily. Fail.
This, that, and the other thing. Everything fail!!
So a permissions issue? Some way to use DSN file after all? Mismatched permission settings in my cnx string? Boneheaded oversight? Something else that I've missed? I'm pretty good at both SQL Server and Access, but only at a basic level in their security stuff and connection strings are the devil.
* retrieved table properties *
Just in case they help, I retrieved these (after objects added to TableDefs collection).
** This one, done in UI and with DSN and this-is-ID-field, worked with editing: **
Name = dbo_tblSendTo
Updatable = False
DateCreated = 4/19/2016 11:11:40 AM
LastUpdated = 4/19/2016 11:11:42 AM
Connect = ODBC;Description=SQL Server tables for TeleSales 5;DRIVER=SQL Server Native Client 10.0;SERVER=(local)\sqlexpress;Trusted_Connection=Yes;APP=Microsoft Office 2003;WSID=CMSERVER;DATABASE=TS5_General;
Attributes = 536870912
SourceTableName = dbo.tblSendTo
RecordCount = -1
ValidationRule =
ValidationText =
ConflictTable =
ReplicaFilter =
** And this one, from table linked via code, didn't: **
Name = tblSendTo
Updatable = False
DateCreated = 4/19/2016 11:17:51 AM
LastUpdated = 4/19/2016 11:17:51 AM
Connect = ODBC;Description=SQL Server tables for TeleSales 5;DRIVER=SQL Server Native Client
> 10.0;SERVER=(local)\sqlexpress;Trusted_Connection=Yes;APP=Microsoft Office 2003;WSID=CMSERVER;DATABASE=TS5_General;
Attributes = 536870912
SourceTableName = dbo.tblSendTo
RecordCount = -1
ValidationRule =
ValidationText =
ConflictTable =
ReplicaFilter =
* my plea *
So..... Please someone help me out. I don't like feeling stupid like this, and regrettably I need to do this instead of replacing it with .NET code or similar.
Thanks, anyone who can...
Ed.
Alas, I am able to answer my own question.
edited a little since first posted in reply to HansUp's comments
I had added an identity column to the table that I couldn't edit. However, I had not set it up as a primary key. It turns out that using identity doesn't make something a primary key automatically.
But the latter, making it primary key using either of the 2 possible DDL syntaxes, is crucial. Since I thought I had dealt with the no edits without unique key problem, I focused on permissions.
All of the permissions things here, then, are just a sideshow.
The upshot of this is to be sure to add an identity column and make it a primary key if for some reason your original table schema didn't have that.
If I have the time, I will be trimming the question to reflect what I've discovered.

Verify Backup SQL Server database using SMO

I'm using Vb.net 2013 and SQL Server 2008 R2.
I have a backup database, and I want to restore with SMO. But before restoring I want to verify if this file that I want to restore is a valid SQL Server database backup and has the identical structure (tables , fields) with the database that my program uses.
I know about the Restore.ReadBackupHeader method, but I don't know what information it holds and how I can use it to test this header with the header of a correct database?
Thank you !
I do it in Powershell with smo and and backupdevise. I scan a directory for certain backupfiles and then check in the header what the name of the database is and where the database is from what server and date and so. Finally I map it to the right database and do a restore.
See microsoft ref: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.backupdevice.readbackupheader(v=sql.105).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
found this VB code
'Declaration
Public Function ReadBackupHeader As DataTable
'Usage
Dim instance As BackupDevice
Dim returnValue As DataTable
returnValue = instance.ReadBackupHeader()
here is other code:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/c8e5b271-f053-4d3e-bd39-3d2a89e55af0/version-of-sql-back-up-file?forum=sqlsmoanddmo
Here is my powershell at github code: https://github.com/patriklindstrom/Powershell-pasen/blob/master/Restore-Sql.ps1
[Microsoft.SqlServer.Management.Smo.BackupDeviceItem]$backupDevice = New-Object ("Microsoft.SqlServer.Management.Smo.BackupDeviceItem") ($f.fullname, "File")
#set param so the PercentComplete notification is updated every 10 sec
$smoRestore.Devices.Add($backupDevice)
$ErrorActionPreference = "SilentlyContinue"
#read db name, original sql server name and date from the backup file's backup header
$smoRestoreDetails = $smoRestore.ReadBackupHeader($server)
$DBNameFromBackup = $smoRestoreDetails.Rows[0]["DatabaseName"]
$DatabaseList += $DBNameFromBackup
$OriginalDBServer = $smoRestoreDetails.Rows[0]["ServerName"]
$OriginalDBBackupDate = $smoRestoreDetails.Rows[0]["BackupFinishDate"]
$smoRestore.Database = $DBNameFromBackup
I would suggesst that you write to the description field version number or key /changeset from code repo when backup is made. Then you check it when you want to restore. You can see Header info with eg RESTORE HEADERONLY FROM DISK = 'E:\SQL\big_DB.bak'. I mean a mean database can contain 1000 tables and 30.000 stored procedures. How should you check them in your restore script?

How to open a Lotus Notes Database on a network share?

I am trying to interact with a Notes database (my personal mail file), via C# and am stuck right out of the gate, trying to open the database with the following code:
string sNotesSourceFolder = "$Inbox";
string sUserName = "scady";
string sMailFile = #"NotesMail_FDrive\ARchives\" + sUserName + ".nsf";
string sServerName = #"Answers1/Answers";
string sPassword = "xxxxxx";
//Creating the notes session
NotesSession session = new NotesSession();
session.Initialize(sPassword);
NotesDatabase NotesDb = session.GetDatabase(sServerName, sMailFile, false);
//If the database is not already open then open it.
if (!NotesDb.IsOpen)
{
NotesDb.Open();
}
calling session.GetDatabase() with the server name and path the the nsf file returns null which, apparently means it could not open the db.
I am running this code locally, against a Notes server called "Answers1/Answers". I am now thinking based on From the Notes server, the nsf files are located on a network fileshare of "NotesMail_FDrive\ARchives\xyz.nsf".
Is the server on which the file resides a Notes server or just a network share?
If it's a network share, you need the drive mapping, but sServerName would be "" to get to a "local" drive. I'm assuming Notesdata1 is a directory on X, so if it's the drive name, remove it.
string sMailFile = #"X:\Notesdata1\NotesMail\ARchives\scady.nsf";
string sServerName = #"";
If it's a Notes server AND Notesdata1 is the data directory for the Notes server, then
string sMailFile = #"NotesMail\ARchives\scady.nsf";
string sServerName = #"Answers1/Answers";
Sorry I didn't notice this on the first pass...
The value for sMailFile should not be "\10.10.10.71\notesdata1\NotesMail\ARchives\scady.nsf". The other two values that you have commented out are also wrong. The arguments for getDatabase are the Domino server's distinguished name, and a relative path. No server IP, no volume, and no "file:" prefix. Just something like this: "archives\scady.nsf". It should be exactly what you see for the path when you open up the database in your Notes client and bring up the database properties dialog.
Previous Answer
Your sServername variable contains the Domino server's distinguished name, as it should.
But why are you using File.Exists? The Domino server wants exclusive access to all of the files in its data tree. If you are actually successfully accessing the file on the Domino server via a filesystem call, then perhaps you are locking the Domino server out of accessing it.
But also: where are you running this code? Are you running it on the same computer, and under the same account that you normally use to read your email? Or on a different computer and account?

dbExpress TSQLConnection param problems

I am new to dbexpress and I cannot figure out how to set the TSQLConnection parm for the SQL Host name at runtime. When I install my program on a client system the TSQLConnectionHost is still reading the Host from my development system that I entered during development.
TSQLConnection.Params is of type TStrings, which means it holds a set of String items. In case of TSQLConnection, Params holds a set of Name=Value pairs, where Name is a parameter name and Value is that parameters value. To read a value of specific parameter, use:
var
s: String;
...
s := SQLConnection1.Params.Values['ParamName'];
To assign a value to specific parameter, use:
SQLConnection1.Params.Values['ParamName'] := 'NewValue';
(Substitute 'ParamName' with actual parameter name and 'NewValue' with actual new value.)
I faced this problem a few years ago when I started developing with dbExpress. On my development machine, the databases were in location X whereas the production machines had the databases in location Y. The way I got around this was to store the physical location of the database in the registry (via a small utility program which I wrote) and then use the following code to load the correct value. The location could be stored in an INI file which would require a slight alteration to my code, but that part is less important.
procedure TDm.SQLConnection1BeforeConnect(Sender: TObject);
var
dir: string;
begin
with TRegIniFile.create (regpath) do // this is where I get the physical value
begin
dir:= ReadString ('firebird', progname, '');
free
end;
with sqlconnection1 do
begin
close;
params.values['database']:= dir;
end;
end;
The reason for your problem is that you have not disconnected the SQLConnection AND all datasets before distrbuting your app.
Things to do
a) make sure all components are NOT connected.
b) set the params of the SQLConnection to blanks.
c) when you app starts, read the required connection params from an ini file, and populate the SQLConnection with those.
d) THEN connect and you will be fine!
Regards
Chris