added back-sql files

This commit is contained in:
Dmitry Kovalev 2000-03-16 19:08:22 +00:00
parent 8dc136ddd6
commit d63b3dc2fd
16 changed files with 1636 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# $OpenLDAP$
SRCS = init.c config.c search.c bind.c other.c \
entry-id.c schema-map.c sql-wrap.c modify.c util.c
OBJS = init.lo config.lo search.lo bind.lo other.lo \
entry-id.lo schema-map.lo sql-wrap.lo modify.lo util.lo
LDAP_INCDIR= ../../../include
LDAP_LIBDIR= ../../../libraries
BUILD_OPT = "--enable-sql"
BUILD_MOD = @BUILD_SQL@
LINKAGE = @BUILD_SQL_DYNAMIC@
LIBBASE = back_sql
XINCPATH = -I.. -I$(srcdir)/.. $(SLAPD_SQL_INCLUDES)
XDEFS = $(MODULES_CPPFLAGS)
XLDFLAGS = $(MODULES_LDFLAGS)
all-local-lib: ../.backend
../.backend: lib$(LIBBASE).a
@touch $@

View File

@ -0,0 +1,39 @@
#ifndef __BACKSQL_H__
#define __BACKSQL_H__
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "external.h"
#include "sql-types.h"
#define BACKSQL_MAX_DN_LEN 255
typedef struct
{
char *dbhost;
int dbport;
char *dbuser;
char *dbpasswd;
char *dbname;
//SQL condition for subtree searches differs in syntax:
//"LIKE CONCAT('%',?)" or "LIKE '%'+?" or smth else
char *subtree_cond;
char *oc_query,*at_query;
char *insentry_query,*delentry_query;
Avlnode *db_conns;
Avlnode *oc_by_name;
Avlnode *oc_by_id;
int schema_loaded;
ldap_pvt_thread_mutex_t dbconn_mutex;
ldap_pvt_thread_mutex_t schema_mutex;
SQLHENV db_env;
}backsql_info;
#endif

View File

@ -0,0 +1,205 @@
# Microsoft Developer Studio Project File - Name="backsql" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 5.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=backsql - Win32 Single Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "backsql.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "backsql.mak" CFG="backsql - Win32 Single Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "backsql - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "backsql - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "backsql - Win32 Single Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "backsql - Win32 Single Release" (based on\
"Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
!IF "$(CFG)" == "backsql - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\..\Release"
# PROP Intermediate_Dir "..\..\..\Release\backsql"
# PROP Target_Dir ""
RSC=rc.exe
# ADD BASE RSC /l 0x419
# ADD RSC /l 0x419
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\\" /I "..\..\..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "backsql - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\..\Debug"
# PROP Intermediate_Dir "..\..\..\Debug\backsql"
# PROP Target_Dir ""
RSC=rc.exe
# ADD BASE RSC /l 0x419
# ADD RSC /l 0x419
# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MTd /W3 /GX /Z7 /Od /I "..\\" /I "..\..\..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "backsql - Win32 Single Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "backsql"
# PROP BASE Intermediate_Dir "backsql"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "..\..\..\SDebug"
# PROP Intermediate_Dir "..\..\..\SDebug\backsql"
# PROP Target_Dir ""
RSC=rc.exe
# ADD BASE RSC /l 0x419
# ADD RSC /l 0x419
# ADD BASE CPP /nologo /MTd /W3 /GX /Z7 /Od /I "..\\" /I "..\..\..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
# ADD CPP /nologo /W3 /GX /Z7 /Od /I "..\\" /I "..\..\..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "backsql - Win32 Single Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "backldb0"
# PROP BASE Intermediate_Dir "backldb0"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\..\..\SRelease"
# PROP Intermediate_Dir "..\..\..\SRelease\backsql"
# PROP Target_Dir ""
RSC=rc.exe
# ADD BASE RSC /l 0x419
# ADD RSC /l 0x419
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "..\\" /I "..\..\..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I "..\\" /I "..\..\..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ENDIF
# Begin Target
# Name "backsql - Win32 Release"
# Name "backsql - Win32 Debug"
# Name "backsql - Win32 Single Debug"
# Name "backsql - Win32 Single Release"
# Begin Source File
SOURCE=".\back-sql.h"
# End Source File
# Begin Source File
SOURCE=.\bind.c
# End Source File
# Begin Source File
SOURCE=.\config.c
# End Source File
# Begin Source File
SOURCE=".\entry-id.c"
# End Source File
# Begin Source File
SOURCE=".\entry-id.h"
# End Source File
# Begin Source File
SOURCE=.\external.h
# End Source File
# Begin Source File
SOURCE=.\init.c
# End Source File
# Begin Source File
SOURCE=.\modify.c
# End Source File
# Begin Source File
SOURCE=.\other.c
# End Source File
# Begin Source File
SOURCE=".\schema-map.c"
# End Source File
# Begin Source File
SOURCE=".\schema-map.h"
# End Source File
# Begin Source File
SOURCE=.\search.c
# End Source File
# Begin Source File
SOURCE=".\sql-types.h"
# End Source File
# Begin Source File
SOURCE=".\sql-wrap.c"
# End Source File
# Begin Source File
SOURCE=".\sql-wrap.h"
# End Source File
# Begin Source File
SOURCE=.\util.c
# End Source File
# Begin Source File
SOURCE=.\util.h
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,35 @@
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "portable.h"
#include <stdio.h>
#include <sys/types.h>
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
int backsql_bind(Backend *be,Connection *conn,Operation *op,
char *dn,char *ndn,int method,char *mech,struct berval *cred,char** edn)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_bind()\n",0,0,0);
//for now, just return OK, allowing to test modify operations
send_ldap_result(conn,op,LDAP_SUCCESS,NULL,NULL,NULL,0);
Debug(LDAP_DEBUG_TRACE,"<==backsql_bind()\n",0,0,0);
return 0;
}
int backsql_unbind(Backend *be,Connection *conn,Operation *op)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_unbind()\n",0,0,0);
backsql_free_db_conn(be,conn);
send_ldap_result(conn,op,LDAP_SUCCESS,NULL,NULL,NULL,0);
Debug(LDAP_DEBUG_TRACE,"<==backsql_unbind()\n",0,0,0);
return 0;
}

View File

@ -0,0 +1,168 @@
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "portable.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
int backsql_db_config(BackendDB *be,const char *fname,int lineno,int argc,char **argv)
{
backsql_info *si=(backsql_info*) be->be_private;
Debug(LDAP_DEBUG_TRACE,"==>backsql_db_config()\n",0,0,0);
if (!si)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_config: be_private is NULL!!!\n",0,0,0);
exit(1);
}
if (!strcasecmp(argv[0],"dbhost"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing hostname in dbhost directive\n",
fname,lineno,0);
}
else
{
si->dbhost=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): hostname=%s\n",si->dbhost,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"dbuser"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing username in dbuser directive\n",
fname,lineno,0);
}
else
{
si->dbuser=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): dbuser=%s\n",argv[1],0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"dbpasswd"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing password in dbpasswd directive\n",
fname,lineno,0);
}
else
{
si->dbpasswd=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): dbpasswd=%s\n",si->dbpasswd,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"dbname"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing database name in dbname directive\n",
fname,lineno,0);
}
else
{
si->dbname=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): dbname=%s\n",si->dbname,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"subtree_cond"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing SQL condition in subtree_cond directive\n",
fname,lineno,0);
}
else
{
si->subtree_cond=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): subtree_cond=%s\n",si->subtree_cond,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"oc_query"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing SQL statement in oc_query directive\n",
fname,lineno,0);
}
else
{
si->oc_query=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): oc_query=%s\n",si->oc_query,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"at_query"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing SQL statement in at_query directive\n",
fname,lineno,0);
}
else
{
si->at_query=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): at_query=%s\n",si->at_query,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"insentry_query"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing SQL statement in insentry_query directive\n",
fname,lineno,0);
}
else
{
si->insentry_query=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): insentry_query=%s\n",si->insentry_query,0,0);
}
return(0);
}
if (!strcasecmp(argv[0],"delentry_query"))
{
if (argc<2)
{
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): missing SQL statement in delentry_query directive\n",
fname,lineno,0);
}
else
{
si->delentry_query=strdup(argv[1]);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config(): delentry_query=%s\n",si->delentry_query,0,0);
}
return(0);
}
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_config (%s line %d): unknown directive '%s' (ignored)\n",
fname,lineno,argv[0]);
return 0;
}

View File

@ -0,0 +1,36 @@
1) driver name comparison for MS SQL Server workaround is realy kinda dirty
hack, but for now i don't know how to code it more carefully
2) another dirty hack: length of LONGVARCHAR and LONGVARBINARY fields is
currently set to MAX_ATTR_LEN. Maybe such fields must be handled with
SQLGetData() instead of SQLBindCol(), but it is said in documentation,
that it is guaranteed to work only when such column goes after last bound
column. Or should we get ALL columns with SQLGetData (then something like
_SQLFetchAsStrings() wrapper would do SQLGetData() for all columns)...
4) in some cases (particularly, when using OpenLink Generic ODBC driver with
MS SQL Server), it returns "Function sequence error" after all records are
fetched. I really don't know what it means, and after all
- it works with any other driver I tried
5) referral handling. this bug actually addresses all backends, as I can
understand. draft-ietf-ldapext-namedref-xx.txt says that referral should be
returned for ANY object containing "ref" attribute. And is_entry_referral
macro designed for "referral" objectclass only. This limits usability of
referrals too much. For instance, I could want to replicate some subtree on
another server, storing just "searchable" attributes + referral to full
object, and then use this subtree as kind of index for query routing.
If search returns referrals only for "referral" entries - I cannot do such
thing
6) DO NOT EVER USE -O2 option (or any other optimization) under Un*x/gcc!!!
I have spent days trying to catch weird bugs, which went gone with optimization off
7) The same thing that works on RedHat 6.0 (glibc 2.1.1), dumps core on
6.1 (glibc 2.1.2) (the same code behaves differently when built on 6.0 and 6.1)
my problem was solved by upgrading iODBC to 3.0 beta - but it is kinda strange
that beta works better than release (and release still works fine on 6.0)
8) Oracle does case-sensitive comparison of strings by default, so back-sql
becomes sensitive too when using Oracle. Later I'll add some option to slapd.conf
that would allow to set some function to process values before comparison
(something like "before_match UPPER", so that back-sql could generate
something like "select ... from ... where ... and UPPER(colname)=UPPER(?) or
UPPER(colname) LIKE UPPER(...)")
9) ldapsearch sometimes refuses to show some attributes ("NOT PRINTABLE" diags)
on Win32 (on linux everything's fine -- at least with mySQL)

View File

@ -0,0 +1,164 @@
CONTENT
1. Purpose
2. Metainformation used
3. Typical back-sql operation
4. Perspectives on back-sql as effective storage backend (not MAPPER)
1. Purpose
Primary purpose of this backend is to PRESENT information stored in some RDBMS
as an LDAP subtree. It is being designed to be tunable to virtually any
relational schema without having to change source. It is NOT designed as backend
that uses RDBMS to store LDAP data (though you can use it for this purpose, it
will definitely be not the most effective way).
But THIS backend primarily targets the situation when you ALREADY HAVE some
data in one or more RDBMSes of one or more different vendors on one or more
different hosts and operating systems, having one or more different
relational schemas. These could be data used by different software, which you
want to integrate, or just local parts of bigger information project. Using
LDAP standalone server with back-sql as middleware, you can integrate this
heterogeneous information as a single distributed LDAP tree, and even organize
data exchange between nodes, not having to worry about unique id's, different
schemas etc (****see authordn attribute in samples, and dts_ldap utility).
Or, you could simply want to export some information like ISP database to LDAP,
to authenticate users, make email lookups or whatever...
2. Metainformation used
***
Almost everything mentioned later is illustrated in example, which is located
in backsql/RDBMS_DEPENDENT directory, and contains scripts for generating sample
database for Oracle,MS SQL Server and mySQL.
***
First thing that one must arrange for himself is what set of objectclasses
can present your RDBMS information. The easiest way is to create objectclass
for each entity you had in ER-diagram when designing your relational schema.
Or you could choose some other way...
Nevertheless, when you think it out, we must define a way to translate LDAP
operation requests to (series of) SQL queries. Let us deal with SEARCH
operation.
Example:
Lets suppose that we store information about persons working in our
organization in two tables:
PERSONS PHONES
---------- -------------
id integer id integer
first_name varchar pers_id integer references persons(id)
last_name varchar phone
middle_name varchar
...
(PHONES contains telephone numbers associated with persons). A person can have
several numbers, then PHONES contains several records with corresponding
pers_id, or no numbers (and no records in PHONES with such pers_id). LDAP
objectclass to present such information could look like this:
person
-------
MUST cn
MAY telephoneNumber
MAY firstName
MAY lastName
...
To fetch all values for cn attribute given person ID, we construct the query:
SELECT CONCAT(persons.first_name,' ',persons.last_name) as cn FROM persons WHERE persons.id=?
for telephoneNumber we can use:
SELECT phones.phone as telephoneNumber FROM persons,phones WHERE persons.id=phones.pers.id and persons.id=?
if we wanted to service LDAP request with filter like (telephoneNumber=123*),
we would construct something like:
SELECT ... FROM persons,phones WHERE persons.id=phones.pers.id and persons.id=? and phones.phone like '123%'
So, if we had information about what tables contain values for given each
attribute, how to join this tables and arrange these values, we could try
to automatically generate such statements, and translate search filters
to SQL clauses
To store such information, we add three more tables to our schema, so that
and fill it with data (see samples):
ldap_objclasses
---------------
id=1
name="person"
keytbl="persons"
keycol="id"
create_proc="{call create_person(?)}"
delete_proc="{call delete_person(?)}"
ldap_attrs
-----------
id=1
oc_id=1
name="cn"
sel_expr="CONCAT(persons.first_name,' ',persons.last_name)"
from_tbls="persons"
join_where=NULL
add_proc=...
delete_proc=...
************
id=<n>
oc_id=1
name="telephoneNumber"
expr="phones.phone"
from_tbls="persons,phones"
join_where="phones.pers_id=persons.id"
add_proc=...
delete_proc=...
ldap_entries
------------
id=1
dn=<dn you choose>
parent=<parent record id>
keyval=<value of primary key>
First two tables contain structured information about constructing queries like
those we made in example. The latter (ldap_entries), contains information about
structure of LDAP tree, referencing actual information by key value. Having
objectclass id, we can determine table and column which contain primary keys,
and load data for the entry attributes using our queries.
3. Typical back-sql operation
Having metainformation loaded, back-sql uses these tables to determine a set
of primary keys of candidates (depending on search scope and filter). It tries
to do it for each objectclass registered in ldap_objclasses.
Exapmle:
for our query with filter (telephoneNumber=123*) we would get following
query (which loads candidate IDs)
SELECT ldap_entries.id,persons.id, 'person' AS objectClass, ldap_entries.dn AS dn FROM ldap_entries,persons,phones WHERE persons.id=ldap_entries.keyval AND ldap_entries.objclass=? AND ldap_entries.parent=? AND phones.pers_id=persons.id AND (phones.phone LIKE '123%')
(for ONELEVEL search)
or "... AND dn=?" (for BASE search)
or "... AND dn LIKE '%?'" (for SUBTREE)
Then, for each candidate, we load attributes requested using per-attribute queries
like
SELECT phones.phone AS telephoneNumber FROM persons,phones WHERE persons.id=? AND phones.pers_id=persons.id
Then, we use test_filter() to test entry for full LDAP search filter match (since
we cannot effectively make sense of SYNTAX of corresponding LDAP schema attribute,
we translate the filter into most relaxed SQL condition to filter candidates),
and send it to user.
ADD,DELETE,MODIFY operations also performed on per-attribute metainformation
(add_proc etc.). In those fields one can specify an SQL statement or stored procedure
call which can add, or delete given value of given attribute, using given entry
keyval (see examples -- mostly ORACLE and MSSQL - since there're no stored procs in mySQL).
4. Perspectives on back-sql as effective storage backend (not MAPPER)
Though as I said, back-sql is intended for presenting existing databases to LDAP,
and as such is not most effective in presenting LDAP data to RDBMS, I have a couple
of ideas on this point, and going to implement this in back-sql using
#ifdefs (one that wants to do RDBMS->LDAP, defines one flag, one that wants
LDAP->RDBMS, defines another).
These tasks have much in common (RDBMS access,connection handling etc), but
latter does not need so much additional metainformation.
For instance, it may have one table for each attribute type in LDAP schema,
and use ldap_entries analog to present tree structure... Later this functionality
will be described more closely...

View File

@ -0,0 +1,38 @@
1. Build
To build slapd with back-sql under Unix you need to build and install
iODBC 2.50.3 (later versions should probably work). Then, run
"configure <options you need> --enable-sql [--with-iodbc-includes=<path>] [--with-iodbc-libs=<path>]",
this should build back-sql-enabled slapd.
Under Win32/MSVC++, I modified the workspace so that back-sql is built into
slapd automatically, since MS odbc32 is included in standard library pack,
and it does no bad even if you don't plan to use it. I also could provide
precompiled executables for those who don't have MSVC later (when back-sql
comes into some stable state).
2. Tune datasources and slapd.conf
Next, you need to define ODBC datasource with data you want to publish
with help of back-sql. Assuming that you have your data in some SQL-compliant
RDBMS, and have installed proper ODBC driver for this RDBMS, this is as simple
as adding a record into odbc.ini (for iODBC), or using ODBC wizard in
Control Panel (for odbc32). Next, you need to add appropriate "database"
record to your slapd.conf. See
sample provided in "back-sql/RDBMS_DEPENDENT/" subdirectory. The only thing
worth noting about this is that "dbname" directive stands for ODBC datasource
name, not the name of your database in RDBMS context.
3. Creating and using back-sql metatables
See SQL scripts and slapd.conf files in sample directory .
Create db/user whatever for test, execute create.sql, create_testdb.sql,
test_data.sql,test_metadata.sql from appropriate directory (use
"mysql < xxx.sql" for mySQL, Query Analyzer+Open query file for MS SQL,
sqlplus and "@xxx.sql" for Oracle)
4. Testing
To diagnose back-sql, run slapd with debug level TRACE ("slapd -d 5" will go).
Then, use some LDAP client to query corresponding subtree (for test database,
you could for instance search one level from "o=sql,c=RU"). I personally used
saucer, which is included in OpenLDAP package (it builds automatically under
Unix/GNU configure and for MSVC I added appropriate project to workspace).
And also Java LDAP browser-editor (see link somewhere on OpenLDAP site) to
test ADD/DELETE/MODIFY operations on Oracle and MS SQL

View File

@ -0,0 +1,18 @@
Platforms and configurations it has been tested on (for now I included only
configurations I've tested personally):
1) slapd on redhat linux 6.0/6.1 (glibc 2.1.1/2.1.2), built with egcs
(versions packaged with appropriate red hat):
iODBC 2.50.3 (on 6.0), 3.0beta (on 6.1),
mySQL (on same linux) 3.22.25,3.22.30 trough myODBC 2.50.23,
MSSQL (on WinNT 4/sp3) 7.0 through OpenLink driver suite 3 (broker on NT),
Personal Oracle (on WinNT4/sp3) 8.0.3 through OpenLink driver suite 3 (broker on NT),
Oracle (on linux 6.0) 8.0.5 through OpenLink driver suite 3 (broker on linux)
2) slapd on WinNT4/sp3, Win98 second edition, Windows2000pre,
built with MSVC++ 5,6:
ODBC32.DLL shipped with appropriate system,
MSSQL (on WinNT4/sp3,Win98,Win2000) 7.0, through its native driver,
Personal Oracle (on WinNT4/sp3,Win98) 8.0.3, through its native driver,
Oracle 7 (on Solaris/Sparc 2.6) through its native driver

View File

@ -0,0 +1,178 @@
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "portable.h"
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
#include "schema-map.h"
#include "entry-id.h"
#include "util.h"
backsql_entryID* backsql_free_entryID(backsql_entryID* id)
{
backsql_entryID* next=id->next;
if (id->dn!=NULL)
free(id->dn);
free(id);
return next;
}
backsql_entryID* backsql_dn2id(backsql_entryID *id,SQLHDBC dbh,char *dn)
{
static char id_query[]="SELECT id,keyval,objclass FROM ldap_entries WHERE dn=?";
SQLHSTMT sth;
BACKSQL_ROW_NTS row;
//SQLINTEGER nrows=0;
RETCODE rc;
Debug(LDAP_DEBUG_TRACE,"==>backsql_dn2id(): dn='%s'\n",dn,0,0);
backsql_Prepare(dbh,&sth,id_query,0);
if ((rc=backsql_BindParamStr(sth,1,dn,BACKSQL_MAX_DN_LEN)) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_dn2id(): error binding dn parameter:\n",0,0,0);
backsql_PrintErrors(SQL_NULL_HENV,dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
return NULL;
}
if ((rc=SQLExecute(sth)) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_dn2id(): error executing query:\n",0,0,0);
backsql_PrintErrors(SQL_NULL_HENV,dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
return NULL;
}
backsql_BindRowAsStrings(sth,&row);
if ((rc=SQLFetch(sth)) == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
{
if (id==NULL)
{
id=(backsql_entryID*)ch_calloc(1,sizeof(backsql_entryID));
}
id->id=atoi(row.cols[0]);
id->keyval=atoi(row.cols[1]);
id->oc_id=atoi(row.cols[2]);
id->dn=strdup(dn);
id->next=NULL;
}
else
id=NULL;
backsql_FreeRow(&row);
SQLFreeStmt(sth, SQL_DROP);
if (id!=NULL)
Debug(LDAP_DEBUG_TRACE,"<==backsql_dn2id(): id=%d\n",(int)id->id,0,0);
else
Debug(LDAP_DEBUG_TRACE,"<==backsql_dn2id(): no match\n",0,0,0);
return id;
}
int backsql_get_attr_vals(backsql_at_map_rec *at,backsql_srch_info *bsi)
{
RETCODE rc;
SQLHSTMT sth;
BACKSQL_ROW_NTS row;
int i;
Debug(LDAP_DEBUG_TRACE,"==>backsql_get_attr_vals(): oc='%s' attr='%s' keyval=%d\n",
bsi->oc->name,at->name,bsi->c_eid->keyval);
if ((rc=backsql_Prepare(bsi->dbh,&sth,at->query,0)) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error preparing query: %s\n",at->query,0,0);
backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
return 1;
}
if (backsql_BindParamID(sth,1,&(bsi->c_eid->keyval)) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error binding key value parameter\n",0,0,0);
return 1;
}
if ((rc=SQLExecute(sth)) != SQL_SUCCESS && rc!= SQL_SUCCESS_WITH_INFO)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error executing query\n",0,0,0);
backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
return 1;
}
backsql_BindRowAsStrings(sth,&row);
while ((rc=SQLFetch(sth)) == SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
{
for (i=0;i<row.ncols;i++)
{
if (row.is_null[i]>0)
{
backsql_entry_addattr(bsi->e,row.col_names[i],row.cols[i],/*row.col_prec[i]*/
strlen(row.cols[i]));
// Debug(LDAP_DEBUG_TRACE,"prec=%d\n",(int)row.col_prec[i],0,0);
}
// else
// Debug(LDAP_DEBUG_TRACE,"NULL value in this row for attribute '%s'\n",row.col_names[i],0,0);
}
}
backsql_FreeRow(&row);
SQLFreeStmt(sth,SQL_DROP);
Debug(LDAP_DEBUG_TRACE,"<==backsql_get_attr_vals()\n",0,0,0);
return 1;
}
Entry* backsql_id2entry(backsql_srch_info *bsi,Entry* e,backsql_entryID* eid)
{
char **c_at_name;
backsql_at_map_rec *at;
Debug(LDAP_DEBUG_TRACE,"==>backsql_id2entry()\n",0,0,0);
bsi->oc=backsql_oc_with_id(bsi->bi,eid->oc_id);
bsi->e=e;
bsi->c_eid=eid;
e->e_attrs=NULL;
if (bsi->base_dn != NULL)
e->e_dn=strdup(bsi->c_eid->dn);
if (bsi->attrs!=NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_id2entry(): custom attribute list\n",0,0,0);
for(c_at_name=bsi->attrs;*c_at_name!=NULL;c_at_name++)
{
if (!strcasecmp(*c_at_name,"objectclass") || !strcasecmp(*c_at_name,"0.10"))
{
//backsql_entry_addattr(bsi->e,"objectclass",bsi->oc->name,strlen(bsi->oc->name));
continue;
}
at=backsql_at_with_name(bsi->oc,*c_at_name);
if (at!=NULL)
backsql_get_attr_vals(at,bsi);
else
Debug(LDAP_DEBUG_TRACE,"backsql_id2entry(): attribute '%s' is not defined for objectlass '%s'\n",
*c_at_name,bsi->oc->name,0);
}
}
else
{
Debug(LDAP_DEBUG_TRACE,"backsql_id2entry(): retrieving all attributes\n",0,0,0);
avl_apply(bsi->oc->attrs,(AVL_APPLY)backsql_get_attr_vals,bsi,0,AVL_INORDER);
}
backsql_entry_addattr(bsi->e,"objectclass",bsi->oc->name,strlen(bsi->oc->name));
Debug(LDAP_DEBUG_TRACE,"<==backsql_id2entry()\n",0,0,0);
return e;
}

View File

@ -0,0 +1,16 @@
#ifndef __BACKSQL_ENTRYID_H__
#define __BACKSQL_ENTRYID_H__
typedef struct __backsql_entryID
{
unsigned long id;
unsigned long keyval;
unsigned long oc_id;
char *dn;
struct __backsql_entryID *next;
}backsql_entryID;
backsql_entryID* backsql_dn2id(backsql_entryID* id,SQLHDBC dbh,char *dn);
backsql_entryID* backsql_free_entryID(backsql_entryID* id);//returns next
#endif

View File

@ -0,0 +1,56 @@
/* $OpenLDAP$ */
#ifndef _SQL_EXTERNAL_H
#define _SQL_EXTERNAL_H
LDAP_BEGIN_DECL
extern int sql_back_initialize LDAP_P(( BackendInfo *bi ));
extern int backsql_destroy LDAP_P(( BackendInfo *bi ));
extern int backsql_db_init LDAP_P(( BackendDB *bd ));
extern int backsql_db_open LDAP_P(( BackendDB *bd ));
extern int backsql_db_close LDAP_P(( BackendDB *bd ));
extern int backsql_db_destroy LDAP_P(( BackendDB *bd ));
extern int backsql_db_config LDAP_P(( BackendDB *bd,
const char *fname, int lineno, int argc, char **argv ));
extern int backsql_bind LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op,
char *dn, char *ndn, int method, char* mech,
struct berval *cred, char** edn ));
extern int backsql_unbind LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op ));
extern int backsql_search LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op, char *base,
char *nbase, int scope, int deref, int sizelimit, int timelimit,
Filter *filter, char *filterstr, char **attrs, int attrsonly ));
extern int backsql_compare LDAP_P((BackendDB *bd,
Connection *conn, Operation *op,
char *dn, char *ndn, Ava *ava ));
extern int backsql_modify LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op,
char *dn, char *ndn, LDAPModList *ml ));
extern int backsql_modrdn LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op,
char *dn, char *ndn, char*newrdn, int deleteoldrdn,
char *newSuperior ));
extern int backsql_add LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op, Entry *e ));
extern int backsql_delete LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op, char *dn, char *ndn ));
extern int backsql_abandon LDAP_P(( BackendDB *bd,
Connection *conn, Operation *op, int msgid ));
LDAP_END_DECL
#endif /* _SQL_EXTERNAL_H */

View File

@ -0,0 +1,191 @@
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "portable.h"
#include <stdio.h>
#include <sys/types.h>
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
#include "schema-map.h"
#include "util.h"
#ifdef SLAPD_SQL_DYNAMIC
int backsql_LTX_init_module(int argc, char *argv[]) {
BackendInfo bi;
memset( &bi, 0, sizeof(bi) );
bi.bi_type = "sql";
bi.bi_init = backbacksql_initialize;
backend_add(&bi);
return 0;
}
#endif /* SLAPD_SHELL_DYNAMIC */
int sql_back_initialize(
BackendInfo *bi
)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_initialize()\n",0,0,0);
bi->bi_open = 0;
bi->bi_config = 0;
bi->bi_close = 0;
bi->bi_destroy = 0;
bi->bi_db_init = backsql_db_init;
bi->bi_db_config = backsql_db_config;
bi->bi_db_open = backsql_db_open;
bi->bi_db_close = backsql_db_close;
bi->bi_db_destroy = backsql_db_destroy;
#ifdef BACKSQL_ALL_DONE
bi->bi_op_abandon = backsql_abandon;
bi->bi_op_compare = backsql_compare;
#else
bi->bi_op_abandon = 0;
bi->bi_op_compare = 0;
#endif
bi->bi_op_bind = backsql_bind;
bi->bi_op_unbind = backsql_unbind;
bi->bi_op_search = backsql_search;
bi->bi_op_modify = backsql_modify;
bi->bi_op_modrdn = backsql_modrdn;
bi->bi_op_add = backsql_add;
bi->bi_op_delete = backsql_delete;
bi->bi_acl_group = 0;
bi->bi_connection_init = 0;
bi->bi_connection_destroy = 0;
Debug(LDAP_DEBUG_TRACE,"<==backsql_initialize()\n",0,0,0);
return 0;
}
int backsql_destroy ( BackendInfo *bi )
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_destroy()\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"<==backsql_destroy()\n",0,0,0);
return 0;
}
int backsql_db_init(BackendDB *bd)
{
backsql_info *si;
Debug(LDAP_DEBUG_TRACE,"==>backsql_db_init()\n",0,0,0);
si = (backsql_info *) ch_calloc( 1, sizeof(backsql_info) );
ldap_pvt_thread_mutex_init(&si->dbconn_mutex);
ldap_pvt_thread_mutex_init(&si->schema_mutex);
backsql_init_db_env(si);
bd->be_private=si;
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_init()\n",0,0,0);
return 0;
}
int backsql_db_destroy(BackendDB *bd)
{
backsql_info *si=(backsql_info*)bd->be_private;
Debug(LDAP_DEBUG_TRACE,"==>backsql_db_destroy()\n",0,0,0);
ldap_pvt_thread_mutex_lock(&si->dbconn_mutex);
backsql_free_db_env(si);
ldap_pvt_thread_mutex_unlock(&si->dbconn_mutex);
ldap_pvt_thread_mutex_lock(&si->schema_mutex);
backsql_destroy_schema_map(si);
ldap_pvt_thread_mutex_unlock(&si->schema_mutex);
ldap_pvt_thread_mutex_destroy(&si->schema_mutex);
ldap_pvt_thread_mutex_destroy(&si->dbconn_mutex);
free(si->dbname);
free(si->dbuser);
if (si->dbpasswd)
free(si->dbpasswd);
if (si->dbhost)
free(si->dbhost);
free(si->subtree_cond);
free(si->oc_query);
free(si->at_query);
free(si->insentry_query);
free(si->delentry_query);
free(si);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_destroy()\n",0,0,0);
return 0;
}
int backsql_db_open (BackendDB *bd)
{
backsql_info *si=(backsql_info*)bd->be_private;
Connection tmp;
SQLHDBC dbh;
Debug(LDAP_DEBUG_TRACE,"==>backsql_db_open(): testing RDBMS connection\n",0,0,0);
if (si->dbname==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): datasource name not specified (use dbname directive in slapd.conf)\n",0,0,0);
return 1;
}
if (si->dbuser==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): user name not specified (use dbuser directive in slapd.conf)\n",0,0,0);
return 1;
}
if (si->subtree_cond==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): subtree search SQL condition not specified (use subtree_cond directive in slapd.conf)\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): setting '%s' as default\n",backsql_def_subtree_cond,0,0);
si->subtree_cond=strdup(backsql_def_subtree_cond);
}
if (si->oc_query==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): objectclass mapping SQL statement not specified (use oc_query directive in slapd.conf)\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): setting '%s' by default\n",backsql_def_oc_query,0,0);
si->oc_query=strdup(backsql_def_oc_query);
}
if (si->at_query==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): attribute mapping SQL statement not specified (use at_query directive in slapd.conf)\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): setting '%s' by default\n",backsql_def_at_query,0,0);
si->at_query=strdup(backsql_def_at_query);
}
if (si->insentry_query==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): entry insertion SQL statement not specified (use insentry_query directive in slapd.conf)\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): setting '%s' by default\n",backsql_def_insentry_query,0,0);
si->insentry_query=strdup(backsql_def_insentry_query);
}
if (si->delentry_query==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): entry deletion SQL statement not specified (use delentry_query directive in slapd.conf)\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): setting '%s' by default\n",backsql_def_delentry_query,0,0);
si->delentry_query=strdup(backsql_def_delentry_query);
}
tmp.c_connid=-1;
dbh=backsql_get_db_conn(bd,&tmp);
if (!dbh)
{
Debug(LDAP_DEBUG_TRACE,"backsql_db_open(): connection failed, exiting\n",0,0,0);
return 1;
}
backsql_free_db_conn(bd,&tmp);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_open(): test succeeded, schema map loaded\n",0,0,0);
return 0;
}
int backsql_db_close(BackendDB *bd)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_db_close()\n",0,0,0);
Debug(LDAP_DEBUG_TRACE,"<==backsql_db_close()\n",0,0,0);
return 0;
}

View File

@ -0,0 +1,409 @@
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include "portable.h"
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include "slap.h"
#include "back-sql.h"
#include "sql-wrap.h"
#include "schema-map.h"
#include "entry-id.h"
#include "util.h"
int backsql_modify(BackendDB *be,Connection *conn,Operation *op,
char *dn,char *ndn,LDAPModList *modlist)
{
backsql_info *bi=(backsql_info*)be->be_private;
SQLHDBC dbh;
SQLHSTMT sth;
RETCODE rc;
backsql_oc_map_rec *oc=NULL;
backsql_entryID e_id,*res;
LDAPModList *c_mod;
backsql_at_map_rec *at=NULL;
struct berval *at_val;
int i;
dn=dn_validate(dn);
Debug(LDAP_DEBUG_TRACE,"==>backsql_modify(): changing entry '%s'\n",dn,0,0);
dbh=backsql_get_db_conn(be,conn);
if (!dbh)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): could not get connection handle - exiting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
res=backsql_dn2id(&e_id,dbh,dn);
if (res==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): could not lookup entry id\n",0,0,0);
send_ldap_result(conn,op,LDAP_NO_SUCH_OBJECT,"",NULL,NULL,NULL);
return 1;
}
oc=backsql_oc_with_id(bi,e_id.oc_id);
if (oc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): cannot determine objectclass of entry -- aborting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLAllocStmt(dbh, &sth);
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): traversing modifications list\n",0,0,0);
for(c_mod=modlist;c_mod!=NULL;c_mod=c_mod->ml_next)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): attribute '%s'\n",c_mod->ml_type,0,0);
at=backsql_at_with_name(oc,c_mod->ml_type);
if (at==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): attribute provided is not registered in this objectclass ('%s')\n",c_mod->ml_type,0,0);
continue;
}
SQLBindParameter(sth,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&e_id.keyval,0,0);
switch(c_mod->ml_op)
{
case LDAP_MOD_REPLACE:
{
char *query;
int qlen;
SQLHSTMT asth;
BACKSQL_ROW_NTS row;
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): replacing values for attribute '%s'\n",at->name,0,0);
if (at->add_proc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): add procedure is not defined for this attribute ('%s') - unable to perform replacements\n",at->name,0,0);
break;
}
del_all:
query=NULL;
qlen=0;
query=backsql_strcat(query,&qlen,"SELECT ",at->sel_expr," AS ",at->name,
" FROM ",at->from_tbls,
" WHERE ",oc->keytbl,".",oc->keycol,"=?",NULL);
if (at->join_where!=NULL && at->join_where[0]!='\0')
query=backsql_strcat(query,&qlen," AND ",at->join_where,NULL);
Debug(LDAP_DEBUG_TRACE,"backsql_modify() constructed query to get all existing values: %s\n",query,0,0);
if ((rc=backsql_Prepare(dbh,&asth,query,0)) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error preparing query\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,asth,rc);
free(query);
break;
}
free(query);
if (backsql_BindParamID(asth,1,&e_id.keyval) != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error binding key value parameter\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,asth,rc);
SQLFreeStmt(asth,SQL_DROP);
break;
}
if ((rc=SQLExecute(asth)) != SQL_SUCCESS && rc!= SQL_SUCCESS_WITH_INFO)
{
Debug(LDAP_DEBUG_TRACE,"backsql_get_attr_values(): error executing attribute query\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,asth,rc);
SQLFreeStmt(asth,SQL_DROP);
break;
}
backsql_BindRowAsStrings(asth,&row);
while ((rc=SQLFetch(asth)) == SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
{
for (i=0;i<row.ncols;i++)
{
SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,row.cols[i],strlen(row.cols[i]),0);
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): executing '%s'\n",at->delete_proc,0,0);
rc=SQLExecDirect(sth,at->delete_proc,SQL_NTS);
if (rc!=SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): delete_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
}
}
}
backsql_FreeRow(&row);
SQLFreeStmt(asth,SQL_DROP);
}
//PASSTHROUGH - to add new attributes -- do NOT add break
case LDAP_MOD_ADD:
if (at->add_proc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): add procedure is not defined for this attribute ('%s')\n",at->name,0,0);
break;
}
if (c_mod->ml_bvalues==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): no values given to add for attribute '%s'\n",at->name,0,0);
break;
}
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): adding new values for attribute '%s'\n",at->name,0,0);
for(i=0,at_val=c_mod->ml_bvalues[0];at_val!=NULL;i++,at_val=c_mod->ml_bvalues[i])
{
//check for syntax here - maybe need binary bind?
SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,at_val->bv_val,at_val->bv_len,0);
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): executing '%s'\n",at->add_proc,0,0);
rc=SQLExecDirect(sth,at->add_proc,SQL_NTS);
if (rc!=SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): add_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
}
}
break;
case LDAP_MOD_DELETE:
if (at->delete_proc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): delete procedure is not defined for this attribute ('%s')\n",at->name,0,0);
break;
}
if (c_mod->ml_bvalues==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): no values given to delete for attribute '%s' -- deleting all values\n",at->name,0,0);
goto del_all;
}
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): deleting values for attribute '%s'\n",at->name,0,0);
for(i=0,at_val=c_mod->ml_bvalues[0];at_val!=NULL;i++,at_val=c_mod->ml_bvalues[i])
{
//check for syntax here - maybe need binary bind?
SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,at_val->bv_val,at_val->bv_len,0);
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): executing '%s'\n",at->delete_proc,0,0);
rc=SQLExecDirect(sth,at->delete_proc,SQL_NTS);
if (rc!=SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_modify(): delete_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
}
}
break;
}
SQLFreeStmt(sth,SQL_RESET_PARAMS);
}
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_SUCCESS,"",NULL,NULL,NULL);
Debug(LDAP_DEBUG_TRACE,"<==backsql_modify()\n",0,0,0);
return 0;
}
int backsql_modrdn(BackendDB *be,Connection *conn,Operation *op,
char *dn,char *ndn,char *newrdn,int deleteoldrdn,char *newSuperior)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_modrdn()\n",0,0,0);
return 0;
}
int backsql_add(BackendDB *be,Connection *conn,Operation *op,Entry *e)
{
backsql_info *bi=(backsql_info*)be->be_private;
SQLHDBC dbh;
SQLHSTMT sth;
unsigned long new_keyval;
long i;
RETCODE rc;
backsql_oc_map_rec *oc=NULL;
backsql_at_map_rec *at_rec=NULL;
backsql_entryID parent_id,*res;
Attribute *at;
struct berval *at_val;
char *pdn;
Debug(LDAP_DEBUG_TRACE,"==>backsql_add(): adding entry '%s'\n",e->e_dn,0,0);
if (dn_validate(e->e_dn)==NULL)
{
Debug(LDAP_DEBUG_TRACE,"==>backsql_add(): invalid dn '%s' -- aborting\n",e->e_dn,0,0);
}
for(at=e->e_attrs;at!=NULL;at=at->a_next)
{
//Debug(LDAP_DEBUG_TRACE,"backsql_add(): scanning entry -- %s\n",at->a_type,0,0);
if (!strcasecmp(at->a_type,"objectclass"))
{
oc=backsql_oc_with_name(bi,at->a_vals[0]->bv_val);
break;
}
}
if (oc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): cannot determine objectclass of entry -- aborting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
if (oc->create_proc == NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): create procedure is not defined for this objectclass - aborting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
dbh=backsql_get_db_conn(be,conn);
if (!dbh)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): could not get connection handle - exiting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLAllocStmt(dbh, &sth);
SQLBindParameter(sth,1,SQL_PARAM_OUTPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&new_keyval,0,0);
//SQLBindParameter(sth,2,SQL_PARAM_OUTPUT,SQL_C_SLONG,SQL_INTEGER,0,0,&retcode,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_add(): executing '%s'\n",oc->create_proc,0,0);
rc=SQLExecDirect(sth,oc->create_proc,SQL_NTS);
if (rc != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): create_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLFreeStmt(sth,SQL_RESET_PARAMS);
Debug(LDAP_DEBUG_TRACE,"backsql_add(): create_proc returned keyval=%d\n",new_keyval,0,0);
for(at=e->e_attrs;at!=NULL;at=at->a_next)
{
at_rec=backsql_at_with_name(oc,at->a_type);
if (at_rec==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): attribute provided is not registered in this objectclass ('%s')\n",at->a_type,0,0);
continue;
}
if (at_rec->add_proc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): add procedure is not defined for this attribute ('%s')\n",at->a_type,0,0);
continue;
}
SQLBindParameter(sth,1,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,&new_keyval,0,0);
for(i=0,at_val=at->a_vals[0];at_val!=NULL;i++,at_val=at->a_vals[i])
{
//if (at->a_syntax==SYNTAX_BIN)
// SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_BINARY,0,0,at_val->bv_val,0,0);
//else
SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,at_val->bv_val,at_val->bv_len,0);
Debug(LDAP_DEBUG_TRACE,"backsql_add(): executing '%s'\n",at_rec->add_proc,0,0);
rc=SQLExecDirect(sth,at_rec->add_proc,SQL_NTS);
if (rc!=SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): add_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
}
}
}
SQLFreeStmt(sth,SQL_RESET_PARAMS);
pdn=dn_parent(be,e->e_dn);
res=backsql_dn2id(&parent_id,dbh,pdn);
if (res==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): could not lookup parent entry for new record ('%s')\n",
pdn,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
free(pdn);
backsql_BindParamStr(sth,1,e->e_dn,BACKSQL_MAX_DN_LEN);
SQLBindParameter(sth,2,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,&oc->id,0,0);
SQLBindParameter(sth,3,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,&parent_id.id,0,0);
SQLBindParameter(sth,4,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,&new_keyval,0,0);
rc=SQLExecDirect(sth,bi->insentry_query,SQL_NTS);
if (rc != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_add(): could not insert ldap_entries record\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
//execute delete_proc to delete data added !!!
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_SUCCESS,"",NULL,NULL,NULL);
return 0;
}
int backsql_delete(BackendDB *be,Connection *conn,Operation *op,
char *dn,char *ndn)
{
backsql_info *bi=(backsql_info*)be->be_private;
SQLHDBC dbh;
SQLHSTMT sth;
RETCODE rc;
backsql_oc_map_rec *oc=NULL;
backsql_entryID e_id,*res;
dn=dn_validate(dn);
Debug(LDAP_DEBUG_TRACE,"==>backsql_delete(): deleting entry '%s'\n",dn,0,0);
dbh=backsql_get_db_conn(be,conn);
if (!dbh)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): could not get connection handle - exiting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
res=backsql_dn2id(&e_id,dbh,dn);
if (res==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): could not lookup entry id\n",0,0,0);
send_ldap_result(conn,op,LDAP_NO_SUCH_OBJECT,"",NULL,NULL,NULL);
return 1;
}
oc=backsql_oc_with_id(bi,e_id.oc_id);
if (oc==NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): cannot determine objectclass of entry -- aborting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
if (oc->delete_proc == NULL)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): delete procedure is not defined for this objectclass - aborting\n",0,0,0);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLAllocStmt(dbh, &sth);
SQLBindParameter(sth,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&e_id.keyval,0,0);
//SQLBindParameter(sth,2,SQL_PARAM_OUTPUT,SQL_C_SLONG,SQL_INTEGER,0,0,&retcode,0,0);
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): executing '%s'\n",oc->delete_proc,0,0);
rc=SQLExecDirect(sth,oc->delete_proc,SQL_NTS);
if (rc != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): delete_proc execution failed\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLFreeStmt(sth,SQL_RESET_PARAMS);
SQLBindParameter(sth,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&e_id.id,0,0);
rc=SQLExecDirect(sth,bi->delentry_query,SQL_NTS);
if (rc != SQL_SUCCESS)
{
Debug(LDAP_DEBUG_TRACE,"backsql_delete(): failed to delete record from ldap_entries\n",0,0,0);
backsql_PrintErrors(bi->db_env,dbh,sth,rc);
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
return 1;
}
SQLFreeStmt(sth,SQL_DROP);
send_ldap_result(conn,op,LDAP_SUCCESS,"",NULL,NULL,NULL);
Debug(LDAP_DEBUG_TRACE,"<==backsql_delete()\n",0,0,0);
return 0;
}

View File

@ -0,0 +1,33 @@
#ifndef __BACKSQL_SCHEMA_MAP_H__
#define __BACKSQL_SCHEMA_MAP_H__
typedef struct
{
char *name;
char *keytbl;
char *keycol;
char *create_proc;//expected to return keyval of newly created entry
char *delete_proc;//supposed to expect keyval as parameter and delete all the attributes as well
unsigned long id;
Avlnode *attrs;
}backsql_oc_map_rec;
typedef struct
{
char *name;//literal name of corresponding LDAP attribute type
char *from_tbls;
char *join_where;
char *sel_expr;
char *add_proc; //supposed to expect 2 binded values: entry keyval and attr. value to add, like "add_name(?,?)"
char *modify_proc; //supposed to expect two binded values: entry keyval and old and new values of attr
char *delete_proc; //supposed to expect 2 binded values: entry keyval and attr. value to delete
char *query; //for optimization purposes attribute load query is preconstructed from parts on schemamap load time
}backsql_at_map_rec;
int backsql_load_schema_map(backsql_info *si,SQLHDBC dbh);
backsql_oc_map_rec* backsql_oc_with_name(backsql_info *si,char* objclass);
backsql_oc_map_rec* backsql_oc_with_id(backsql_info *si,unsigned long id);
backsql_at_map_rec* backsql_at_with_name(backsql_oc_map_rec* objclass,char* attr);
int backsql_destroy_schema_map(backsql_info *si);
#endif

View File

@ -0,0 +1,25 @@
#ifndef __BACKSQL_SQL_TYPES_H__
#define __BACKSQL_SQL_TYPES_H__
/*
* Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
*
* Redistribution and use in source and binary forms are permitted only
* as authorized by the OpenLDAP Public License. A copy of this
* license is available at http://www.OpenLDAP.org/license.html or
* in file LICENSE in the top-level directory of the distribution.
*/
#include <sql.h>
#include <sqlext.h>
typedef struct
{
SWORD ncols;
char** col_names;
UDWORD *col_prec;
char** cols;
SQLINTEGER* is_null;
}BACKSQL_ROW_NTS;
#endif