sdbr.3 (7040B)
1 .TH SDBR 3 2 .SH NAME 3 sdbr_add, sdbr_arg2r, sdbr_attr, sdbr_close, sdbr_dup, sdbr_edit, sdbr_edit2, sdbr_escape, sdbr_init, sdbr_match, sdbr_n, sdbr_print, sdbr_query, sdbr_r2str, sdbr_str2r, sdbr_val, sdbr_zero, vsdbr_join \- simple database records 4 .SH SYNOPSIS 5 .B #include <u.h> 6 .br 7 .B #include <libc.h> 8 .br 9 .B #include <bio.h> 10 .br 11 .B #include <str.h> 12 .br 13 .B #include <vec.h> 14 .br 15 .B #include <sdbr.h> 16 .PP 17 .ft L 18 .nf 19 .ta \w'\fL 'u +\w'\fLString 'u 20 typedef struct { 21 String *attr; 22 String *val; 23 } Sdbr; 24 .fi 25 .PP 26 .ta \w'\fLvoid 'u 27 .B 28 void sdbr_add(Sdbr *r, String attr, String val) 29 .PP 30 .B 31 void sdbr_arg2r(Sdbr *r, char *argv[]) 32 .PP 33 .B 34 int sdbr_attr(Sdbr r, char *attr) 35 .PP 36 .B 37 void sdbr_close(Sdbr *r) 38 .PP 39 .B 40 void sdbr_dup(Sdbr *sr, Sdbr r) 41 .PP 42 .B 43 void sdbr_edit(Sdbr *sr, Sdbr r) 44 .PP 45 .B 46 void sdbr_edit2(Sdbr *r, String attr, String val) 47 .PP 48 .B 49 char* sdbr_escape(String *s, String val) 50 .PP 51 .B 52 void sdbr_init(Sdbr *r) 53 .PP 54 .B 55 char sdbr_match(char *k, char *v) 56 .PP 57 .B 58 ulong sdbr_n(Sdbr r) 59 .PP 60 .B 61 void sdbr_print(Sdbr r) 62 .PP 63 .B 64 int sdbr_query(Sdbr r, Sdbr q) 65 .PP 66 .B 67 void sdbr_r2str(String *s, Sdbr r) 68 .PP 69 .B 70 void sdbr_str2r(Sdbr *sr, String s) 71 .PP 72 .B 73 String sdbr_val(Sdbr r, char *attr) 74 .PP 75 .B 76 void sdbr_zero(Sdbr *r) 77 .PP 78 .B 79 void vsdbr_join(Sdbr *v[], Sdbr r) 80 .SH DESCRIPTION 81 These functions provide a simple database record system for storing and querying 82 attribute-value pairs using the same format as Plan 9's network database (ndb). 83 An 84 .B Sdbr 85 record contains vectors of attribute names and their corresponding values, 86 both stored as dynamic strings. 87 While ndb is designed specifically for network configuration, 88 sdb records can be used for any general-purpose attribute-value data. 89 .PP 90 .I Sdbr_init 91 initializes a record 92 .IR r . 93 The record is initially empty. 94 .PP 95 .I Sdbr_close 96 frees all memory associated with record 97 .IR r . 98 .PP 99 .I Sdbr_add 100 adds an attribute-value pair to record 101 .IR r . 102 The attribute name is 103 .I attr 104 and its value is 105 .IR val . 106 .PP 107 .I Sdbr_n 108 returns the number of attribute-value pairs in record 109 .IR r . 110 .PP 111 .I Sdbr_zero 112 removes all attribute-value pairs from record 113 .I r 114 but keeps the record structure intact for reuse. 115 .PP 116 .I Sdbr_dup 117 copies all attribute-value pairs from record 118 .I src 119 to record 120 .IR dst . 121 .PP 122 .I Sdbr_r2str 123 formats record 124 .I r 125 as a string and stores it in string 126 .IR s . 127 Each attribute-value pair is formatted as attribute=value, 128 with values quoted and escaped as needed using 129 .IR sdbr_escape . 130 Pairs are separated by tabs and terminated with a newline. 131 .PP 132 .I Sdbr_str2r 133 parses a string 134 .I s 135 containing space-separated attribute=value pairs and adds them to record 136 .IR r . 137 The input string is not modified. 138 Value quoting and escaping are described in 139 .MR sdb (7) . 140 .PP 141 .I Sdbr_arg2r 142 parses command-line arguments 143 .I argv 144 (null-terminated array) as alternating attribute-value pairs and adds them to record 145 .IR r . 146 Arguments are processed in pairs where argv[0] is the attribute and argv[1] is the value. 147 .PP 148 .I Sdbr_val 149 returns the value of attribute 150 .I attr 151 in record 152 .IR r . 153 If the attribute is not found, it returns an empty string. 154 The attribute name can contain wildcards. 155 .PP 156 .I Sdbr_attr 157 returns the index of attribute 158 .I attr 159 in record 160 .IR r , 161 or -1 if not found. 162 .PP 163 .I Sdbr_match 164 tests whether 165 .I value 166 matches 167 .IR pattern . 168 Pattern matching is described in 169 .MR sdb (7) . 170 .PP 171 .I Sdbr_query 172 tests whether record 173 .I r 174 matches all conditions specified in query record 175 .IR query . 176 It returns 1 if the record matches, 0 otherwise. 177 Attribute names in the query starting with '!' are negated conditions. 178 .PP 179 .I Sdbr_edit 180 applies edits from record 181 .I r 182 to record 183 .IR sr . 184 For each attribute in 185 .IR r , 186 the corresponding value in 187 .I sr 188 is updated or added. 189 .PP 190 .I Sdbr_edit2 191 updates or adds a single attribute-value pair in record 192 .IR r . 193 .PP 194 .I Sdbr_escape 195 formats string 196 .I val 197 for output, escaping special characters and storing the result in string 198 .IR s . 199 It returns a pointer to the formatted string. 200 .PP 201 .I Sdbr_print 202 prints record 203 .I r 204 to standard output in attribute=value format. 205 The record must have at least one attribute. 206 .PP 207 .I Vsdbr_join 208 adds record 209 .I r 210 to the vector of records 211 .IR v . 212 The vector must be initialized with 213 .I Vecinitf(&v, sdbr_init, sdbr_close) 214 to properly manage the Sdbr structures. 215 If a record in 216 .I v 217 already has the same first attribute name and value as 218 .IR r , 219 that record is updated with all attributes from 220 .IR r . 221 Otherwise, 222 .I r 223 is appended to the vector. 224 The record 225 .I r 226 must have at least one attribute. 227 .SH EXAMPLES 228 .PP 229 Basic record operations: 230 .IP 231 .EX 232 Sdbr r; 233 234 sdbr_init(&r); 235 sdbr_add(&r, Str("name"), Str("John")); 236 sdbr_add(&r, Str("age"), Str("30")); 237 sdbr_print(r); 238 sdbr_close(&r); 239 .EE 240 .PP 241 Parsing from string: 242 .IP 243 .EX 244 Sdbr r; 245 String s; 246 247 sdbr_init(&r); 248 s = Str("name=John age=30 city=\\"New York\\""); 249 sdbr_str2r(&r, s); 250 print("Name: %s\en", sdbr_val(r, "name").s); 251 sdbr_close(&r); 252 .EE 253 .PP 254 Querying records: 255 .IP 256 .EX 257 Sdbr r, q; 258 259 sdbr_init(&r); 260 sdbr_init(&q); 261 sdbr_add(&r, Str("type"), Str("user")); 262 sdbr_add(&r, Str("status"), Str("active")); 263 sdbr_add(&q, Str("type"), Str("user")); 264 sdbr_add(&q, Str("!status"), Str("inactive")); 265 if(sdbr_query(r, q)) 266 print("Record matches query\en"); 267 sdbr_close(&r); 268 sdbr_close(&q); 269 .EE 270 .PP 271 Pattern matching: 272 .IP 273 .EX 274 if(sdbr_match("*user", "admin_user")) 275 print("Pattern matches\en"); 276 if(sdbr_match(">=100", "150")) 277 print("Comparison matches\en"); 278 .EE 279 .PP 280 Process tracking: 281 .IP 282 .EX 283 Sdbr r; 284 285 sdbr_init(&r); 286 sdbr_str2r(&r, Str("pid=1234 cmd=rc user=glenda cpu=0.5")); 287 sdbr_add(&r, Str("tags"), Str("shell,interactive")); 288 sdbr_add(&r, Str("started"), Str("2025-01-15T10:30:00")); 289 sdbr_print(r); 290 sdbr_close(&r); 291 .EE 292 .PP 293 Configuration with inheritance: 294 .IP 295 .EX 296 Sdbr def, usr; 297 298 sdbr_init(&def); 299 sdbr_init(&usr); 300 sdbr_str2r(&def, Str("editor=sam theme=light font=lucsans")); 301 sdbr_str2r(&usr, Str("theme=dark font=go")); 302 sdbr_edit(&def, usr); 303 print("Font: %s\en", sdbr_val(def, "font").s); 304 sdbr_close(&def); 305 sdbr_close(&usr); 306 .EE 307 .PP 308 Date range queries: 309 .IP 310 .EX 311 Sdbr ev, q; 312 313 sdbr_init(&ev); 314 sdbr_init(&q); 315 sdbr_str2r(&ev, Str("timestamp=2025-01-15T14:30:00 level=error")); 316 sdbr_str2r(&q, Str( 317 "timestamp=>=2025-01-15T14:00:00 " 318 "timestamp=<=2025-01-15T15:00:00")); 319 if(sdbr_query(ev, q)) 320 print("Event in time range\en"); 321 sdbr_close(&ev); 322 sdbr_close(&q); 323 .EE 324 .PP 325 Geographic range queries: 326 .IP 327 .EX 328 Sdbr loc, q; 329 330 sdbr_init(&loc); 331 sdbr_init(&q); 332 sdbr_str2r(&loc, Str("lat=40.7128 lon=-74.0060 city=\"New York\"")); 333 sdbr_str2r(&q, Str( 334 "lat=>=40.0 lat=<=41.0 " 335 "lon=>=-73.0 lon=<=-75.0")); 336 if(sdbr_query(loc, q)) 337 print("Location in bounding box\en"); 338 sdbr_close(&loc); 339 sdbr_close(&q); 340 .EE 341 .SH SOURCE 342 .B \*9/src/libsdbr 343 .SH SEE ALSO 344 .MR ndb (3) , 345 .MR ndb (7) , 346 .MR sdb (1) , 347 .MR sdb (3) , 348 .MR str (3) , 349 .MR vec (3) 350 .SH DIAGNOSTICS 351 Functions call 352 .I sysfatal 353 if memory allocation fails. 354 .I Sdbr_print , 355 .I sdbr_r2str , 356 and 357 .I vsdbr_join 358 call 359 .I sysfatal 360 if passed an empty record. 361 .SH BUGS 362 None.