def unlock(self): """ Unlock the record """ # Locking is done via an M level routine on the record global g_path = self._dd.m_closed_form(self._rowid) M.mexec(str("LOCK -%s" % g_path)) # TODO: mexec to take unicode
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 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 lock(self, timeout=5): """ Lock a record. """ g_path = self._dd.m_closed_form(self._rowid) # Set the timeout M.Globals["DILOCKTM"].value = timeout # use DILF^LOCK function to perform the lock M.proc("LOCK^DILF", g_path) # result is returned in $T rv, = M.mexec("set l0=$T", M.INOUT(0)) if rv != 1: raise FilemanLockFailed(filename=self._dd.filename, row=self._rowid, timeout=timeout)
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 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 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])
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])