def fm_validate_insert(self, value): """ This validator checks the field using the Fileman logic. Since it expects value to be in Fileman External format and we are using Internal Format, it is of limited use. Also, I don't know how it will work on a sub-file. """ M.Globals["ERR"].kill() # Validates single field against the data dictionary s0, = M.proc("CHK^DIE", self.fileid, self.fieldid, "H", value, M.INOUT(""), "ERR") err = M.Globals["ERR"] # s0 should contain ^ for error, internal value for valid data if s0 == "^": error_code = err['DIERR'][1].value error_msg = '\n'.join( [v for k, v in err['DIERR'][1]['TEXT'].items()]) help_msg = [v for k, v in err['DIHELP'].items()] raise FilemanError( """DBSDD.fm_validate_insert(): fileid = [%s], fieldid = [%s], value = [%s], error_code = [%s], error_msg = [%s], help = [%s]""" % (self.fileid, self.fieldid, value, error_code, error_msg, help_msg)) # If err exists, then some form of programming error if err.exists(): raise FilemanError( """DBSDD.fm_validate_insert(): fileid = [%s], fieldid = [%s], value = [%s], err = [%s]""" % (self.fileid, self.fieldid, value, '\n'.join(err)))
def fileid(self): """ Look up the ^DIC array and find the file number for the specified file, e.g. FILE = 1 - result is a string. """ if self._fileid is None: rv = M.mexec('''set s1=$order(^DIC("B",s0,0))''', str(self.filename[:30]), M.INOUT(""))[0] if rv != '': self._fileid = rv return self._fileid
def list_files(self): """ Oddly, I cannot see how to list the files using the DBS API. This is required for debugging etc. """ if self.remote: return self.remote.list_files() M.mset('DUZ',self.DUZ) M.mset('U', "^") if self.isProgrammer: M.mset('DUZ(0)', "@") rv = [] s0 = "0" while s0 != "": s0, name = M.mexec( '''set s0=$order(^DIC(s0)) Q:s0'=+s0 I $D(^DIC(s0,0))&$D(^DIC(s0,0,"GL"))&$$VFILE^DILFD(s0) S s1=$P(^DIC(s0,0),U,1)''', M.INOUT(s0), M.INOUT("")) if name: rv.append((name, s0)) return rv
def next(self): lastkey = self.lastkey lastrowid = self.lastrowid if self.ascending: asc = 1 else: asc = -1 # TODO: Fileman seems to structure indices with keys in the global path # or in the value - need to investigate further # There is a mad collation approach in M, where numbers sort before non-numbers. # this really messes up the keys. # How should I search? # There is an inefficiency here it takes three searches to find the next record. while 1: if lastrowid is None: # locate the next matching index value lastkey, = M.mexec( """set s0=$order(%ss0),%s)""" % (self.gl, asc), M.INOUT(str(lastkey))) if lastkey == "": break if self.ascending: if self.from_value is not None: if self.from_rule == ">" and lastkey <= self.from_value: continue if self.from_rule == ">=" and lastkey < self.from_value: assert 0 if self.to_value is not None: if self.to_rule == "<=" and lastkey > self.to_value: break if self.to_rule == "=" and lastkey != self.to_value: break if self.to_rule == "<" and lastkey >= self.to_value: break self.lastkey = lastkey lastrowid = "0" else: # descending if self.from_value is not None: if self.from_rule == "<" and lastkey >= self.from_value: continue if self.from_rule == "<=" and lastkey > self.from_value: assert 0 if self.to_value is not None: if self.to_rule == ">=" and lastkey < self.to_value: break if self.to_rule == "=" and lastkey != self.to_value: break if self.to_rule == ">" and lastkey <= self.to_value: break self.lastkey = lastkey lastrowid = "" # Have the key, get the first matching rowid lastrowid, = M.mexec( """set s0=$order(%s"%s",s1),%d)""" % (self.gl, self.lastkey, asc), M.INOUT(str(lastkey)), lastrowid) if lastrowid == "": # No match lastrowid = None continue if self.filters: # Are filters to be applied? if not self.filters(lastrowid): continue if self.skip_rows > 0: self.skip_rows -= 1 continue self.lastrowid = lastrowid if self.raw: return self.lastkey, self.lastrowid return self.getter(self.lastrowid) raise StopIteration
def next(self): # Have we exceeded limit if self.limit and self.results_returned >= self.limit: raise StopIteration lastrowid = self.lastrowid # This value should be a string throughout. if self.ascending: asc = 1 else: asc = -1 while not self.results_complete: # If this is the first pass, we may have the id of a record, which needs to # be verified found = False if self.first_pass: self.first_pass = False if lastrowid is None and asc == -1: lastrowid, = M.mexec( """set s0=$order(%ss0),-1)""" % self.gl, M.INOUT('%')) if valid_rowid(lastrowid): found = True elif lastrowid and float(lastrowid) > 0: row_exists, = M.mexec( """set s0=$data(%ss0))""" % (self.gl), M.INOUT(lastrowid)) if valid_rowid(row_exists): found = True if not found: lastrowid, = M.mexec( """set s0=$order(%ss0),%d)""" % (self.gl, asc), M.INOUT(lastrowid)) if not valid_rowid(lastrowid): break # Check boundary values f_lastrowid = float(lastrowid) if self.ascending: if self.from_rowid is not None: if f_lastrowid == self.from_rowid and self.from_rule == ">": continue if self.to_rowid is not None: if f_lastrowid >= self.to_rowid and self.to_rule == "<": break if f_lastrowid > self.to_rowid and self.to_rule == "<=": break else: # descending: if f_lastrowid == 0: break # header record if self.from_rowid is not None: if f_lastrowid == self.from_rowid and self.from_rule == "<": continue if self.to_rowid is not None: if f_lastrowid <= self.to_rowid and self.to_rule == ">": break if f_lastrowid < self.to_rowid and self.to_rule == ">=": break if self.filters: # Are filters to be applied? if not self.filters(lastrowid): continue if self.skip_rows > 0: self.skip_rows -= 1 continue self.lastrowid = lastrowid self.results_returned += 1 if self.raw: return self.lastrowid return self.getter(self.lastrowid) self.results_complete = True raise StopIteration
def indices(self): """ Return a list of the indices To be valid, the index must be both in the IX ^DD(200,0,"IX","AASWB",200,654)="" """ if self._indices is None: i = [] # TODO: this is not right for multi-column keys # TODO: new style indexes global_name = '^DD(%s,0,"IX","0")' % self.fileid prefix = '^DD(%s,0,"IX",' % self.fileid while 1: global_name = M.mexec('set s0=$query(%s)' % global_name, M.INOUT(""))[0] if not global_name or not global_name.startswith(prefix): break suffix = global_name[len(prefix):-1] parts = suffix.split(",") idx_name = parts[0][1:-1] idx_table = parts[1] idx_columns = parts[2:] index = Index(idx_name, idx_table, idx_columns) i.append(index) # A second list, gives indices for a field columns = {} for idx in i: for c in idx.columns: columns[c] = 1 # Now trawl the listed columns in the data dictionary, and load their # cross references. cr_names = {} for c in columns.keys(): idx_root = M.Globals["^DD"][self.fileid][c][1] if not idx_root[0].exists(): continue for cr_id, val in idx_root.keys_with_decendants(): if float(cr_id) > 0: cr_header = idx_root[cr_id][0].value parts = cr_header.split("^") if len(parts) == 2 and parts[ 1]: # if more than 2 parts, assume MUMPs trigger f = cr_names.get(parts[1], list()) f.append(c) cr_names[parts[1]] = f # Now, just delete items from the index list if they are not in cr_names self._indices = [] for index in i: cr = cr_names.get(index.name) if cr: # verify columns - lots of errors in real systems if len(cr) == len(index.columns): invalid = False for c in cr: if c not in index.columns: invalid = True continue if not invalid: self._indices.append(index) return self._indices
def index_order_traversal(gl_prefix, index, ranges=None, ascending=True, sf_path=[], explain=False): """ A generator which will traverse an index. The iterator should yield rowids. Indices are stored: GLOBAL,INDEXID,VALUE,ROWID="" ^DIZ(999900,"B","hello there from unit test2",183)="" ^DIZ(999900,"B","hello there from unit test2",184)="" ^DIZ(999900,"B","hello there from unit test2",185)="" ^DIZ(999900,"B","record 1",1)="" """ gl = gl_prefix + '"%s",' % index if ranges: r = ranges[0] from_value = r['from_value'] to_value = r['to_value'] from_rule = r['from_rule'] to_rule = r['to_rule'] else: from_value, to_value, from_rule, to_rule = None, None, None, None if explain: yield "index_order_traversal, ascending=%s, gl=%s, index=%s, X %s '%s' AND X %s '%s'" % ( ascending, gl, index, from_rule, from_value, to_rule, to_value) return if from_value != None and to_value != None: if ascending: assert (from_value <= to_value) else: assert (to_value <= from_value) if from_value is None: if ascending: lastkey = " " else: lastkey = "ZZZZZZZZZZZZZZ" lastrowid = "" else: lastkey = from_value if from_rule == '>': lastrowid = None # looks for the next key after lastkey else: lastrowid = '' # looks for the lastkey if ascending: asc = 1 else: asc = -1 # TODO: Fileman seems to structure indices with keys in the global path # or in the value - need to investigate further # There is a mad collation approach in M, where numbers sort before non-numbers. # this really messes up the keys. # How should I search? # There is an inefficiency here it takes three searches to find the next record. while 1: if lastrowid is None: # locate the next matching index value lastkey, = M.mexec("""set s0=$order(%ss0),%s)""" % (gl, asc), M.INOUT(str(lastkey))) if lastkey == "": break if ascending: if from_value is not None: if from_rule == ">" and lastkey <= from_value: continue if from_rule == ">=" and lastkey < from_value: assert 0 if to_value is not None: if to_rule == "<=" and lastkey > to_value: break if to_rule == "=" and lastkey != to_value: break if to_rule == "<" and lastkey >= to_value: break lastkey = lastkey lastrowid = "0" else: # descending if from_value is not None: if from_rule == "<" and lastkey >= from_value: continue if from_rule == "<=" and lastkey > from_value: assert 0 if to_value is not None: if to_rule == ">=" and lastkey < to_value: break if to_rule == "=" and lastkey != to_value: break if to_rule == ">" and lastkey <= to_value: break lastkey = lastkey lastrowid = "" # Have the key, get the first matching rowid lastrowid, = M.mexec( """set s0=$order(%s"%s",s1),%d)""" % (gl, lastkey, asc), M.INOUT(str(lastkey)), lastrowid) if lastrowid == "": # No match lastrowid = None continue yield (lastrowid, "%s%s)" % (gl_prefix, lastrowid), sf_path + [lastrowid])
def file_order_traversal(gl, ranges=None, ascending=True, sf_path=[], explain=False): """ Originate records by traversing the file in file order (i.e. no index) """ if ranges: r = ranges[0] from_rowid = r['from_value'] to_rowid = r['to_value'] from_rule = r['from_rule'] to_rule = r['to_rule'] else: from_rowid, to_rowid, from_rule, to_rule = None, None, None, None if explain: yield "file_order_traversal, ascending=%s, gl=%s, X %s %s AND X %s %s" % ( ascending, gl, from_rule, from_rowid, to_rule, to_rowid) return # the new person file has non-integer user ids if from_rowid != None: from_rowid = float(from_rowid) if to_rowid != None: to_rowid = float(to_rowid) if from_rowid != None and to_rowid != None: if ascending: assert (from_rowid <= to_rowid) else: assert (to_rowid <= from_rowid) if from_rowid is None: if ascending: lastrowid = "0" else: lastrowid = None else: # TODO: I have this in code in shared lastrowid = ('%f' % from_rowid).rstrip('0').rstrip('.').lstrip('0') if from_rowid > 0 and lastrowid[0] == "0": lastrowid = lastrowid[1:] if lastrowid.endswith(".0"): lastrowid = lastrowid[:-2] first_pass = True if ascending: asc = 1 else: asc = -1 while 1: # If this is the first pass, we may have the id of a record, which needs to # be verified found = False if first_pass: first_pass = False if lastrowid is None and asc == -1: lastrowid, = M.mexec("""set s0=$order(%ss0),-1)""" % gl, M.INOUT('%')) if valid_rowid(lastrowid): found = True elif lastrowid and float(lastrowid) > 0: row_exists, = M.mexec("""set s0=$data(%ss0))""" % (gl), M.INOUT(lastrowid)) if valid_rowid(row_exists): found = True if not found: lastrowid, = M.mexec("""set s0=$order(%ss0),%d)""" % (gl, asc), M.INOUT(lastrowid)) if not valid_rowid(lastrowid): break # Check boundary values f_lastrowid = float(lastrowid) if ascending: if from_rowid is not None: if f_lastrowid == from_rowid and from_rule == ">": continue if to_rowid is not None: if f_lastrowid >= to_rowid and to_rule == "<": break if f_lastrowid > to_rowid and to_rule == "<=": break else: # descending: if f_lastrowid == 0: break # header record if from_rowid is not None: if f_lastrowid == from_rowid and from_rule == "<": continue if to_rowid is not None: if f_lastrowid <= to_rowid and to_rule == ">": break if f_lastrowid < to_rowid and to_rule == ">=": break # If this is a subfile, I need to return the full path. yield (lastrowid, "%s%s)" % (gl, lastrowid), sf_path + [lastrowid])