]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/docs/concept
fix case-insensitive matching
[openldap] / servers / slapd / back-sql / docs / concept
1 CONTENT
2 1. Purpose
3 2. Metainformation used
4 3. Typical back-sql operation
5 4. Several important common techniques (referrals, multiclassing)
6
7 1. Purpose
8 Primary purpose of this backend is to PRESENT information stored in some RDBMS
9 as an LDAP subtree without any programming (some SQL and maybe stored
10 procedures can't be considered programming, anyway ;).
11
12 That is, for example, when you (some ISP) have account
13 information you use in RDBMS, and want to use modern solutions that expect such
14 information in LDAP (to authenticate users, make email lookups etc.).
15 Or you want to syncronize or distribute information between different
16 sites/applications that use RDBMSes and/or LDAP. Or whatever else...
17
18 It is NOT designed as general-purpose backend that uses RDBMS instead of
19 BerkeleyDB (as standard back-ldbm does), though it can be used as such
20 with several limitations. You can take a look at
21 http://www.openldap.org/faq/index.cgi?file=378 
22 (OpenLDAP FAQ-O-Matic/General LDAP FAQ/Directories vs. conventional databases)
23 to find out more on this point.
24
25 The idea (detailed below) is to use some metainformation to translate
26 LDAP queries to SQL queries, leaving relational schema untouched, so that
27 old applications can continue using it without any modifications. This allows
28 SQL and LDAP applications to interoperate without replication, and exchange
29 data as needed.
30
31 Back-sql is designed to be tunable to virtually any relational schema without
32 having to change source (through that metainformation mentioned).
33 Also, it uses ODBC to connect to RDBMSes, and is highly configurable for
34 SQL dialects RDBMSes may use, so it may be used for integration and 
35 distribution of data on different RDBMSes, OSes, hosts etc., in other words,
36 in highly heterogeneous environment.
37
38 2. Metainformation used
39 ***
40 Almost everything mentioned later is illustrated in example, which is located
41 in backsql/rdbms_depend directory, and contains scripts for generating sample
42 database for Oracle,MS SQL Server and mySQL.
43 ***
44 First thing that one must arrange for himself is what set of LDAP 
45 objectclasses can present your RDBMS information.
46
47 The easiest way is to create objectclass for each entity you had
48 in ER-diagram when designing your relational schema.
49 Any relational schema, no matter how normalized it is, was designed after
50 some model of your applications domain (for instance, accounts, services etc.
51 in ISP), and is used in terms of its entities, not just tables of normalized
52 schema.
53 It means that for every attribute of every such instance there is an effective
54 SQL query that loads it's values.
55
56 Also you might want your objectclasses to conform to some of standard schemas
57 like inetOrgPerson etc..
58
59 Nevertheless, when you think it out, we must define a way to translate LDAP
60 operation requests to (series of) SQL queries. Let us deal with SEARCH 
61 operation.
62
63 Example:
64 Lets suppose that we store information about persons working in our 
65 organization in two tables:
66
67 PERSONS                 PHONES
68 ----------           -------------
69 id integer              id integer
70 first_name varchar      pers_id integer references persons(id)
71 last_name varchar       phone
72 middle_name varchar
73 ...
74
75 (PHONES contains telephone numbers associated with persons). A person can have
76 several numbers, then PHONES contains several records with corresponding 
77 pers_id, or no numbers (and no records in PHONES with such pers_id). LDAP
78 objectclass to present such information could look like this:
79 person
80 -------
81 MUST cn
82 MAY telephoneNumber
83 MAY firstName
84 MAY lastName
85 ...
86
87 To fetch all values for cn attribute given person ID, we construct the query:
88 SELECT CONCAT(persons.first_name,' ',persons.last_name) as cn FROM persons WHERE persons.id=?
89
90 for telephoneNumber we can use:
91 SELECT phones.phone as telephoneNumber FROM persons,phones WHERE persons.id=phones.pers.id and persons.id=?
92
93 If we wanted to service LDAP request with filter like (telephoneNumber=123*),
94 we would construct something like:
95 SELECT ... FROM persons,phones WHERE persons.id=phones.pers.id and persons.id=? and phones.phone like '123%'
96
97 So, if we had information about what tables contain values for each 
98 attribute, how to join this tables and arrange these values, we could try
99 to automatically generate such statements, and translate search filters
100 to SQL WHERE clauses.
101
102 To store such information, we add three more tables to our schema, so that
103  and fill it with data (see samples):
104
105 ldap_oc_mappings (some columns are not listed for clarity)
106 ---------------
107 id=1
108 name="person"
109 keytbl="persons"
110 keycol="id"
111
112 This table defines mapping between objectclass (its name held in "name" column),
113 and table that holds primary key for corresponding entities. For instance,
114 in our example, the person entity, which we are trying to present as "person"
115 objectclass, resides in two tables (persons and phones), and is identified
116 by persons.id column (that we will call primary key for this entity).
117 keytbl and keycol thus contain "persons" (name of the table), and "id" (name
118 of the column).
119
120 ldap_attr_mappings (some columns are not listed for clarity)
121 -----------
122 id=1
123 oc_id=1
124 name="cn"
125 sel_expr="CONCAT(persons.first_name,' ',persons.last_name)"
126 from_tbls="persons"
127 join_where=NULL
128 ************
129 id=<n>
130 oc_map_id=1
131 name="telephoneNumber"
132 sel_expr="phones.phone"
133 from_tbls="persons,phones"
134 join_where="phones.pers_id=persons.id"
135
136 This table defines mappings between LDAP attributes and SQL queries that
137 load their values. Note that, unlike LDAP schema, these are not *attribute types*
138 - attribute "cn" for "person" objectclass can well have it's values in different
139 table than "cn" for other objectclass, so attribute mappings depend on
140 objectclass mappings (unlike attribute types in LDAP schema, which are
141 indifferent to objectclasses). Thus, we have oc_map_id column with link to
142 oc_mappings table. 
143 Now we cut the SQL query that loads values for given attribute into 3 parts.
144 First goes into sel_expr column - this is the expression we had between
145 SELECT and FROM keywords, which defines WHAT to load.
146 Next is table list - text between FROM and WHERE keywords. It may contain
147 aliases for convenience (see exapmles).
148 The last is part of where clause, which (if exists at all) express the
149 condition for joining the table containing values wich table containing
150 primary key (foreign key equality and such). If values are in the same table
151 with primary key, then this column is left NULL (as for cn attribute above).
152
153 Having this information in parts, we are able to not only construct queries
154 that load attribute values by id of entry (for this we could store SQL query
155 as a whole), but to construct queries that load id's of objects that
156 correspond to given search filter (or at least part of it).
157 See below for examples.
158
159 ldap_entries
160 ------------
161 id=1
162 dn=<dn you choose>
163 oc_map_id=...
164 parent=<parent record id>
165 keyval=<value of primary key>
166
167 This table defines mappings between DNs of entries in your LDAP tree,
168 and values of primary keys for corresponding relational data. It has
169 recursive structure (parent column references id column of the same table),
170 which allows you to add any tree structure(s) to your flat relational data.
171 Having id of objectclass mapping, we can determine table and column for
172 primary key, and keyval stores value of it, thus defining exact tuple
173 corresponding to LDAP entry with this DN.
174
175 Note that such design (see exact SQL table creation query) implies one
176 important constraint - the key must be integer. But all that I know about
177 well-designed schemas makes me think that it s not very narrow ;)
178 If anyone needs support for different types for keys - he may want to write
179 a patch, and submit it to OpenLDAP ITS, then I'll include it.
180
181 Also, several people complained that they don't really need very structured
182 tree, and they don't want to update one more table every time they add or
183 delete instance in relational schema. Those can use a view instead of real
184 table, something like this:
185
186
187 Robin Elfrink wrote:
188
189 > About using a view for ldap_entries...
190 >
191 > This is what I came up with this morning:
192 >
193 > CREATE VIEW ldap_entries (id, dn, oc_map_id, parent, keyval) AS
194 >         SELECT (1000000000+userid),
195 > UPPER(CONCAT(CONCAT('cn=',gecos),',o=MyCompany,c=NL'))
196 > , 1, 0, userid FROM unixusers UNION
197 >         SELECT (2000000000+groupnummer),
198 > UPPER(CONCAT(CONCAT('cn=',groupnaam),',o=MyCompany,c=NL')), 2, 0,
199 > groupnummer FROM groups;
200 >
201
202
203 3. Typical back-sql operation
204 Having metainformation loaded, back-sql uses these tables to determine a set
205 of primary keys of candidates (depending on search scope and filter). It tries
206 to do it for each objectclass registered in ldap_objclasses.
207 Exapmle:
208 for our query with filter (telephoneNumber=123*) we would get following 
209 query generated (which loads candidate IDs)
210 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%')
211 (for ONELEVEL search)
212 or "... AND dn=?" (for BASE search)
213 or "... AND dn LIKE '%?'" (for SUBTREE)
214
215 Then, for each candidate, we load attributes requested using per-attribute queries
216 like
217
218 SELECT phones.phone AS telephoneNumber FROM persons,phones WHERE persons.id=? AND phones.pers_id=persons.id
219
220 Then, we use test_filter() from frontend API to test entry for full
221 LDAP search filter match (since we cannot effectively make sense of SYNTAX
222 of corresponding LDAP schema attribute, we translate the filter into most relaxed
223 SQL condition to filter candidates), and send it to user.
224
225 ADD,DELETE,MODIFY operations also performed on per-attribute metainformation
226 (add_proc etc.). In those fields one can specify an SQL statement or stored procedure
227 call which can add, or delete given value of given attribute, using given entry
228 keyval (see examples -- mostly ORACLE and MSSQL - since there're no stored procs in mySQL).
229
230 We just add more columns to oc_m,appings and attr_mappings, holding statements
231 to execute (like create_proc, add_proc, del_proc etc.), and flags governing
232 order of parameters passed to those statements.
233 Please see samples to find out what are the parameters passed, and other
234 information on this matter - they are self-explanatory for those familiar
235 with concept expressed above.
236
237 4. Several common techniques (referrals, multiclassing etc.)
238 First of all, lets remember that among other major differences to complete
239 LDAP data model, the concept above does not directly support such things
240 as multiple objectclasses for entry, and referrals.
241 Fortunately, they are easy to adopt in this scheme. Back-sql suggests two more
242 tables being added to schema - 
243 ldap_entry_objectclasses(entry_id, oc_name), and ldap_referrals (entry_id,url).
244
245 First contains any number of objectclass names that corresponding entries
246 will be found by, in addition to that mentioned in mapping. Back-sql
247 automatically adds attribute mapping for "objectclass" attribute to each objectclass
248 mapping, that loads values from this table. So, you may, for instance, have
249 mapping for inetOrgPerson, and use it for queries for "person" objectclass...
250
251 Second table contains any number of referrals associated with given entry.
252 Back-sql automatically adds attribute mapping for "ref" attribute to each
253 objectclass mapping, that loads values from this table.
254 So, if you add objectclass "referral" to this entry, and make one or more
255 tuples in ldap_referrals for this entry (they will be seen as values of "ref"
256 attribute), you will have slapd return referral, as described in Administrators
257 Guide.