Source code for adapya.adabas.api

# -*- coding: latin1 -*-
"""
adapya.adabas.api - Adabas Programming Interface
================================================

The adapya.adabas.api module defines the Adabas and Adabasx classes which implement
the Adabas API.

"""
from __future__ import print_function          # PY3

__date__='$Date: 2023-12-01 00:54:33 +0100 (Fri, 01 Dec 2023) $'
__revision__='$Rev: 1072 $'

import getpass # for getuser()
import os      # for getpid()
import platform # cinfo uses platform.uname()
import socket  # for gethostname()
import string
import struct
import sys
import time
import ctypes
from ctypes import c_int, c_char_p, sizeof
import logging
# import adapya.base
from adapya.base import defs # for variables dummymutex,logopt,logstr
from adapya.base.defs import Abuf,adalog
from adapya.base.defs import LOGCMD,LOGBEFORE,LOGCB,LOGFB,LOGRB,LOGSB,LOGVB
from adapya.base.defs import LOGIB,LOGMB,LOGPB,LOGUB,LOGRSP,LOGBUF,LOGSP
from adapya.base.conv import str2ebc
from adapya.base.datamap import Datamap, Uint1, Uint2, Uint4, Uint8, String, \
    Char, Int2, Int4, Bytes, T_NONE, T_STCK, T_GMT, T_HEX, T_NWBO, T_EBCDIC, \
    T_VAR1, fpack, NATIVEBO, NETWORKBO
from adapya.base.dump import dump
from . import adaerror

# fix Python2 difference: make iterator's next() methods available
# as next() function -- copied from six
try:
    advance_iterator = next
except NameError:
    def advance_iterator(it):
        return it.next()
next = advance_iterator

if sys.platform in ('win32','cli'): # CPython or IronPython
    import ctypes.util
    adalname = ctypes.util.find_library('adalnkx') # get full path of adalnkxsa
    # print('adalnkx=%r' % adalname)
else:
    adalname = 'libadalnkx.so'

try:
    adalink=ctypes.cdll.LoadLibrary(adalname)
except OSError:
    print('Running Python Version %s\n\ton platform %s, %d bit, byteorder=%s' % (
         sys.version, sys.platform, sizeof(c_char_p)*8, sys.byteorder ))
    print('"%s" could not be loaded: check that Adabas Client Library (ACL) directory is in path' %(adalname,))
    raise

if sys.platform != 'zos':
    adalink.AdaSetParameter.argtypes = [c_char_p]
    adalink.AdaSetTimeout.argtypes = [c_int,c_int]
    adalink.adabas.argtypes = [c_char_p,c_char_p,c_char_p,c_char_p,c_char_p,c_char_p]
    adalink.lnk_set_adabas_id.argtypes = [c_char_p]
    adalink.lnk_set_uid_pw.argtypes = [c_int,c_char_p,c_char_p]     # acl V6.5 used for LUW Adabas databases only

def adaSetTimeout( sec ):
    if sys.platform == 'zos':     # function not available on zos
        return 0
    else:
        # set Adabas timeout value in Adalnk
        return adalink.AdaSetTimeout(0, sec)

def adaSetParameter( parm):
    # set parameter in Adalnk
    return adalink.AdaSetParameter(parm)


HOBF=1  # High-order-byte first big-endian
LOBF=2  # low-order-byte first little-endian
if sys.byteorder == 'big':
    nativeByteOrder=HOBF
    UNICODE_INTERNAL='utf_16_be'
else:   # little
    nativeByteOrder=LOBF
    UNICODE_INTERNAL='utf_16_le'


[docs]def getip(family=socket.AF_INET,host='10.255.255.255'): """Determine own IP address""" s = socket.socket(family, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect((host, 1)) IP = s.getsockname()[0] # leave off port number except Exception: IP = '127.0.0.1' finally: s.close() return IP
debug = 0 # Adabas file access modes ACC=0 UPD=1 ONL=2 EXU=3 EXF=4 PRV=1 open_modes = (('ACC', 0), ('UPD', 0), ('ONL', PRV), ('EXU', 0), ('EXF', 0))
[docs]class Error(Exception): pass
[docs]class Warning(Exception): pass
[docs]class NotYetImplementedError(Exception): pass
[docs]class ProgrammingError(Exception): pass
[docs]class InvalidSearchString(Exception): pass # used in Adabas.search()
# AdabasException class introduced for Python 3.0 support
[docs]class AdabasException(Exception): """ Instance will have set the following values: self.value is the Adabas response string self.apa is the Adabas call parameters that were used when the error occurred Example on how to call it:: if subclassed e.g. with class DatabaseError(AdabasException) try: raise AdabasException(value,apa) except AdabasException as e: adalog.warning('AdabasException', e.value, e.__class__) dump(e.apa.acb,log=adalog.warning) """ def __init__(self, value, apa): self.value = value self.apa = apa def __str__(self): return repr(self.value)
# class DatabaseError(Exception): pass # Adabas Response # class DataEnd(Exception): pass # Adabas Response Code 3 EOF # class InterfaceError(Exception): pass # passed only one string value # # for Python 3.0 changed to #
[docs]class DatabaseError(AdabasException): pass # Adabas Response other than 0 or 3
[docs]class DataEnd(AdabasException): pass # Adabas Response Code 3 EOF
# Adalinkx Interface Error
[docs]class InterfaceError(AdabasException): pass # passes call parameters
# not used yet: needs classification of response code (adaresp) # class InternalError(DatabaseError): pass # class OperationalError(DatabaseError): pass # class IntegrityError(DatabaseError): pass # class DataError(DatabaseError): pass # class NotSupportedError(DatabaseError): pass # # Acb - classic Adabas control block # ACBLEN = 0x50
[docs]class Acb(Datamap): def __init__(self, **kw): fields=( Uint1( 'typ'), String( 'rsv1', 1), String( 'cmd', 2), String( 'cid', 4), Int4( 'cidn', repos=-4), ## redefines cid numeric Int2( 'fnr'), ## changed from Uint2 for INFOBUFFER V8.2 Uint2( 'rsp'), Uint2( 'dbid', repos=-2), ## redefines rsp Uint4( 'isn'), Uint4( 'isl'), Uint4( 'isq'), Int2( 'fbl'), Int2( 'rbl'), Int2( 'sbl'), Int2( 'vbl'), Int2( 'ibl'), String( 'op1', 1), String( 'op2', 1), String( 'ad1', 8), Uint4( 'ad2'), Uint2( 'lcmp', repos=-4), ## redefines ad2 Uint2( 'ldec'), ## String( 'ad3', 8), String( 'ad4', 8), String( 'ad5', 8), Uint4( 'cmdt'), Uint4( 'usr'), Uint2( 'pdbid', repos=-4), ## redefines usr Uint2( 'pnucid') ## ) Datamap.__init__(self, 'Acb', *fields, **kw) # reset ACB fields if buffer was given if self.buffer: cb=self # shorthand cb.typ=0 cb.cid=' ' cb.fnr=0 cb.rsp=0 # return cb.isn=0 # return cb.isl=0 # return cb.isq=0 # return cb.fbl=0 cb.rbl=0 cb.sbl=0 cb.vbl=0 cb.ibl=0 cb.op1=' ' cb.op2=' ' cb.ad1=' ' cb.ad2=0 # return if response code cb.ad3=' ' # password cb.ad4=' ' # cipher code cb.ad5=' ' cb.cmdt=0 # cb.usr=0 cb.pdbid=0 cb.pnucid=0
# # acbx - extended Adabas control block # ACBXV2 = 'F2' # Type ADACBX, Version 2 ACBXLEN = 0xC0
[docs]class Acbx(Datamap): def __init__(self, **kw): fields=( Uint1( 'typ'), Char( 'rsv1'), String( 'ver', 2), Int2( 'len'), String( 'cmd', 2), Uint2( 'nid'), ## no network byte order Int2( 'rsp'), String( 'cid', 4), Int4( 'cidn', repos=-4), ## redefines cid numeric Uint4( 'dbid'), Int4( 'fnr'), Uint8( 'isn'), Uint8( 'isl'), Uint8( 'isq'), String( 'op1', 1), String( 'op2', 1), String( 'op3', 1), String( 'op4', 1), String( 'op5', 1), String( 'op6', 1), String( 'op7', 1), String( 'op8', 1), String( 'op1_8', 8, repos=-8), String( 'ad1', 8), Uint4( 'ad2'), String( 'ad3', 8), String( 'ad4', 8), String( 'ad5', 8), String( 'ad6', 8), String( 'rsv3', 4), Uint8( 'erra'), # x68 error offset in buffer String( 'errb', 2), # x70 Field name or offset Bytes( 'errbb', 2, repos=-2), Uint2( 'errc'), # x72 Subcode Char( 'errd'), # x74 Buffer type Bytes( 'erre', 1), # x75 Uint2( 'errf'), # Buffer number Uint2( 'subr'), Uint2( 'subs'), String( 'subt', 4), Uint8( 'lcmp'), Uint8( 'ldec'), Uint8( 'cmdt'), # command time in 1/4096 micro sec units Bytes( 'usr', 16), Bytes( 'rsv4', 24), Uint2( 'pdbid', repos=-(16+24), opt=T_NWBO), ## redefines usr ## network byte order *Temp* Uint2( 'pnucid', opt=T_NWBO) ## network byte order *Temp* ) Datamap.__init__(self, 'Acbx', *fields, **kw)
# abdx - Extended Adabas Buffer Descriptor ABDXL = 48 ABDXV2 = 'G2' # Type ADABDX, Version 2 # Values for abdx.loc ABDXSTD = ' ' # buffer at end of ABDX (standard) ABDXIND = 'I' # Indirectly addressed # Values for abdx.id ABDXF = 'F' # Format buffer ABDXR = 'R' # Record buffer ABDXM = 'M' # Multifetch buffer ABDXS = 'S' # Search buffer ABDXV = 'V' # Value buffer ABDXI = 'I' # ISN buffer ABDXP = 'P' # Performance buffer ABDXU = 'U' # User buffer
[docs]class Abdx(Datamap): def __init__(self, **kw): abdx32_fields=( Int2( 'len'), String( 'ver', 2), String( 'id', 1), Bytes( 'rsv1', 1), String( 'loc', 1), Bytes( 'rsv2', 1), Int4( 'rsv3'), Int4( 'rsv4'), Uint8( 'size'), Uint8( 'send'), # send size Uint8( 'recv'), # receive size Int4( 'rsv5'), Uint4( 'addr') ) abdx64_fields=( Int2( 'len'), String( 'ver', 2), String( 'id', 1), Bytes( 'rsv1', 1), String( 'loc', 1), Bytes( 'rsv2', 1), Int4( 'rsv3'), Int4( 'rsv4'), Uint8( 'size'), Uint8( 'send'), # send size Uint8( 'recv'), # receive size Uint8( 'addr') # 64 bit addresses ) if struct.calcsize('P')==8: # 64 bit addresses Datamap.__init__(self, 'Abdx', *abdx64_fields, **kw) else: Datamap.__init__(self, 'Abdx', *abdx32_fields, **kw)
[docs]class ClientInfoLuw(Datamap): def __init__(self, **kw): Datamap.__init__(self,'clientInfoLuw', String('eye',4), # 'REVD' String('ver',2), # version Bytes( 'reserved1',2,opt=T_NONE), String('appl',8), # application name String('prog',8), # program name String('uid',8), # userid String('stmt',4), # statement number Uint4( 'level'), # call level of program Uint8( 'callcnt'), # number of Adabas calls since I/O Uint8( 'execcnt'), # number of times object executed String('lib',8), # library curent object String('rpcclid',8), # RPC client id String('rpcid',16), # RPC id String('rpcconvid',16), # RPC conversation id String('secgroup',8), # Security group String('eye2',4), # 'AUDL' String('ver2',2), # version field String('date',8), # date YYYYMMDD String('time',6), # time HHMMSS String( 'varspace',512,opt=T_NONE), # space for variable fields String('user_name',0,opt=T_VAR1,repos=-128), String('program_name',0,opt=T_VAR1), String('machine_name',0,opt=T_VAR1), String('operating_system_name',0,opt=T_VAR1), String('operating_system_version',0,opt=T_VAR1), String('hardware_name',0,opt=T_VAR1), String('host_name',0,opt=T_VAR1), String('tcpip_address',0,opt=T_VAR1), **kw)
ADAID_SL2 = 2 # adaid structure level w/o timestamp ADAID_SL3 = 3 # adaid structure level with timestamp ADAIDL = 32 # size of adaid structure changed from 24, sl3 fits all
[docs]class Adaid(Datamap): def __init__(self, **kw): Datamap.__init__(self, 'Adaid', Int2( 'level'), Int2( 'size'), String( 'node', 8), String( 'user', 8), Uint4( 'pid'), Uint8( 'timestamp'), # set at open call **kw)
[docs]class Adasafinfo(Datamap): def __init__(self, **kw): Datamap.__init__(self, 'Adasafinfo', String( 'userid', 8), String( 'password', 8), String( 'newpassword', 8), **kw)
ADASAFINFOL=24 # 8+2*100 # 24 ADASAFX = 0xd8 if 0: # need to distinguish 32 or 64 bit class Adaeax(Datamap): def __init__(self, **kw): Datamap.__init__(self, 'Adaeax', Uint1( 'type'), Uint1( 'level'), Uint1( 'reserved1',opt=T_NONE), Uint1( 'rdaarch'), Uint4( 'flags'), Uint8( 'timeout'), # user level timeout (msec) ? CE_UPLONG # adaid comes here 32bytes Uint8( 'eaxuservalue'), # 64 bit addresses Uint8( 'eaxcallbackreply'), # 64 bit addresses **kw)
[docs]class Mcbuh(Datamap): """ Multicall Buffer Header with offsets to global or array of elements """ def __init__(self, buffer=None, offset=0): Datamap.__init__(self, 'MulticallBufferHeader', Uint2('goff'), # offset of global area Uint2('moff'), # offset of multiple areas in one buffer buffer=buffer, offset=offset) if buffer: # reset fields if underlying buffer exists self.goff = self.moff = 0
[docs]class Mcstoff(Datamap): """ Multicall Buffer set up start offset array """ def __init__(self, buffer=None, offset=0, nc=0): Datamap.__init__(self, 'MCBufferStartOffsetArray', Int2('offs',occurs=nc), # start offset array buffer=buffer, offset=offset) if buffer: # reset fields if underlying buffer exists for i in range(nc): self.offs[i] = 0 # reset offsets initially
#o mc update first unused byte in buffer
[docs]class Mfhdr(Datamap): def __init__(self, buffer=None, offset=0): Datamap.__init__(self, 'MultifetchHeader', Uint4('elecount'), buffer=buffer, offset=offset)
[docs]class Mfele(Datamap): def __init__(self, buffer=None, offset=0): Datamap.__init__(self, 'MultifetchElement', Uint4('reclen'), Uint4('rsp'), Uint4('isn'), # PE Index for L9 Uint4('isq'), # Number of index values for L9 buffer=buffer, offset=offset)
# Adabas open command architecture bits - different from Network architecture bits!!! # These bits indicate how the application wants to send/receive the data AOCBSW = 1 # Byte swap AOCEBC = 2 # EBCDIC AOCVAX = 4 # VAX floating point AOCI3E = 8 # IEEE floating point # Network Architecture bits returned in ISL after OP-command # = self.dbarchit RDAABSW =0x01 # Byte swap RDAAEBC =0x04 # EBCDIC RDAASC8 =0x08 # ASCII8 RDAAFVAX=0x10 # VAX floating point RDAAFI3E=0x20 # IEEE floating point def archit2str(arc): a1='High-order-byte-first' a2='ASCII' a3='IBM-float' if arc & RDAABSW: a1='Low-order-byte-first' if arc & RDAAEBC: a2='EBCDIC' if arc & RDAAFVAX: a3='VAX-float' if arc & RDAAFI3E: a3='IEEE-float' return a1+'/'+a2+'/'+a3
[docs]def setsaf(userid,password,newpass='',encrypter=None): """Set session userid and password with ADASAF databases. :param userid: user id for security system where database resides :param password: security system password :param encrypter: optional call back routine that encrypts logon data matching installation-defined decryption in remote database The data is used in the Adalnk for login to a protected database request userid and password. """ safib = Abuf(ADASAFINFOL) safi = Adasafinfo(buffer=safib) safi.userid=userid # safi.password=password # # leave at \x00 as set from Abuf() if newpass: safi.newpassword=newpass if encrypter: encrypter(safib) else: ii = 16 # 108 if newpass: ii+=8 # 100 if sys.hexversion < 0x03010100: # PY2.7 ctypes.c_char needs string -> chr() for i in range(ii): safib[i] = chr(ord(safib[i])^ADASAFX) else: for i in range(ii): safib[i] = ord(safib[i])^ADASAFX if 0: dump(safib,'safib') i = adalink.AdaSetSaf(safib) return i
[docs]def cbyte(key): "condense key string to one byte" n=1 for i in range(len(key)): n += ord(key[i]) * i return n & 0xff
[docs]def setuidpw(dbid, userid, password, encrypter=None): """Set session userid and password with LUW databases. Starting with LUW Adabas 6.5 :param dbid: database id to which userid and password apply :param userid: user id for security system where database resides :param password: security system password :param encrypter: optional call back routine that encrypts logon data matching installation-defined decryption in remote database The data is used in the Adalnk for login to a protected database request userid and password. """ safuid = Abuf(9) safpw = Abuf(9) safuid.write_text(userid) safpw.write_text(password) # leave at \x00 as set from Abuf() # otherwise taken as newpassword function: # safi.newpassword='' if 0: if encrypter: encrypter(safib) else: if sys.hexversion < 0x03010100: for i in range(8): # PY2 ctypes.c_char needs string -> chr() safib[i] = chr(ord(safib[i])^ADASAFX) safib[8+i] = chr(ord(safib[8+i])^ADASAFX) # newpassword not used in this function: # safib[16+i] = chr(ord(safib[16+i])^ADASAFX) else: for i in range(8): safib[i] = ord(safib[i])^ADASAFX safib[8+i] = ord(safib[8+i])^ADASAFX # newpassword not used in this function: # safib[16+i] = ord(safib[16+i])^ADASAFX i = adalink.lnk_set_uid_pw(dbid, safuid, safpw) return i
[docs]def s1(s, byteorder='@'): """return string s converted to bytes after 1 byte inclusive length :param s: input string or (bytes,bytearray) :param byteorder: byteorder for input unicode string may be '<' little or '>' big endian, default is unicode internal """ if byteorder in '>!': enco = 'UTF_16BE' elif byteorder=='<': enco = 'UTF_16LE' else: # byteorder in '@=' enco = UNICODE_INTERNAL if sys.hexversion < 0x03010100: if isinstance(s, unicode): s = s.encode(enco) else: if isinstance(s, str): s = s.encode(enco) elif not isinstance(s, (bytes,bytearray)): raise ProgrammingError("Cannot wrap %s in s1(), need type of str, bytes or bytearray"% (type(s),)) assert len(s) < 255, 'Length of string is %d for s1(), exceeds 254 bytes'%len(s) return struct.pack('B',len(s)+1)+s
[docs]def s2(s, byteorder='@'): """return string s after 2 bytes inclusive length byteorder may be '<' little or '>' big endian """ if byteorder in '>!': enco = 'UTF_16BE' elif byteorder=='<': enco = 'UTF_16LE' else: # byteorder in '@=' enco = UNICODE_INTERNAL if sys.hexversion < 0x03010100: if isinstance(s, unicode): s = s.encode(enco) else: if isinstance(s, str): s = s.encode(enco) elif not isinstance(s, (bytes,bytearray)): raise ProgrammingError("Cannot wrap %s in s4(), need type of str, bytes or bytearray"% (type(s),)) assert len(s) < 16382, 'Length of string is %d for s1(), exceeds 16381 bytes'%len(s) return struct.pack('%sH'%byteorder,len(s)+2)+s
[docs]def s4(s, byteorder='@'): "return string s after 4 bytes inclusive length" if byteorder in '>!': enco = 'UTF_16BE' elif byteorder=='<': enco = 'UTF_16LE' else: # byteorder in '@=' enco = UNICODE_INTERNAL if sys.hexversion < 0x03010100: if isinstance(s, unicode): s = s.encode(enco) else: if isinstance(s, str): s = s.encode(enco) elif not isinstance(s, (bytes,bytearray)): raise ProgrammingError("Cannot wrap %s in s4(), need type of str, bytes or bytearray"% (type(s),)) return struct.pack('%sl'%byteorder,len(s)+4)+s
[docs]def sl4(s, byteorder='@'): "return string with packed length of string. Used for AAL elements" size=1 if sys.hexversion < 0x03010100: if isinstance(s, unicode): size=2 else: if isinstance(s, str): size=2 elif not isinstance(s, (bytes,bytearray)): raise ProgrammingError("Cannot wrap %s in s4(), need type of str, bytes or bytearray"% (type(s),)) return struct.pack('%sl'%byteorder, len(s)*size)
#----------------------------------------------------------------------
[docs]class Adabas(object): """ Define the data structures and methods for Adabas database access. These are the Adabas control block and buffers for the Adabas call() and high level methods for the Adabas API :param fbl: format buffer length - fb will be allocated of that size :param rbl: record buffer length - rb will be allocated of that size :param sbl: search buffer length - sb will be allocated of that size :param vbl: value buffer length - vb will be allocated of that size :param ibl: ISN buffer length - ib will be allocated of that size >>> from adapya.adabas.api import * >>> a=Adabas(fbl=12,rbl=100) # create control block, format and record buffer >>> a.cb.dbid=8 # set database id >>> a.cb.fnr=11 # set file number >>> a.fb.value='AA,AB.' # select interesting fields >>> a.getiseq() # get ISN in sequence (I option) >>> print(a.rb.value) 50005500ALEXANDRE BRAUN See the __init__() method for parameter details of creating an instance of the Adabas class. """
[docs] def newbuffer(self,type,size): """ resize or define new buffer of <type> in ACB style if current size > size: old buffer will be reused :param type: one of ('F','R','S','V','I') :param size: if > 0: allocate buffer and set it for for further reference in call parameters """ cb = self.cb if type == 'F': if cb.fbl < size: cb.fbl = size self.fb = Abuf(size) elif type == 'R': if cb.rbl < size: cb.rbl = size self.rb = Abuf(size) elif type == 'S': if cb.sbl < size: cb.sbl = size self.sb = Abuf(size) elif type == 'V': if cb.vbl < size: cb.vbl = size self.vb = Abuf(size) elif type == 'I': if cb.ibl < size: cb.ibl = size self.ib = Abuf(size) else: raise ProgrammingError("Invalid Adabas call buffer type %s"%type,self)
def __init__(self, fbl=0, rbl=0, sbl=0, vbl=0, ibl=0, pmutex=None, noexceptions=0, thread=0, multifetch=0, archit=None, password='', cipher=''): global totalCalls gg = globals() if 'totalCalls' not in gg: totalCalls=0 self.dbid = 0 self.password = password self.pmutex = pmutex if pmutex else defs.dummymutex self.sub1 = 0 self.sub2 = 0 self.cidseq = 0 # automatic cid count (should be user related) self.cipher = '' # cipher code self.expected_responses = [] # list of response/subcode tuples consumed on each call() self.noexceptions = noexceptions # do no fire exceptions after Adabas call with response code self.updates = 0 # number of updates in transaction (store,delete,update) self.rdaarch = archit # architecture of adabas buffers self.bo = NATIVEBO self.ebcdic = 0 self.encoding = 'latin1' # default buffer encoding unless architecture is EBCDIC self.dbarchit = None # archit returned from database OP call if archit and (archit & RDAAEBC) and not (archit & RDAABSW): # mainframe native calls self.bo = NETWORKBO self.ebcdic = 1 self.encoding = 'cp037' # EBCDIC US (Latin1 characterset) self.rdaarch = RDAAEBC elif not archit and sys.platform == 'zos': self.ebcdic = 1 self.encoding = 'cp037' # EBCDIC US (Latin1 characterset) self.rdaarch = RDAAEBC self.acb=Abuf(ACBLEN) self.cb=Acb(buffer=self.acb, ebcdic=self.ebcdic, byteOrder=self.bo) cb=self.cb # shorthand # Acb fields are formatted in Acb() initialization since buffer was given # set fields specific to Adabas initialization cb.typ=0x30 # X30 call cb.typ=0x30 # X30 call cb.fbl=fbl cb.rbl=rbl cb.sbl=sbl cb.vbl=vbl cb.ibl=ibl self.fb=Abuf(fbl,encoding=self.encoding) if fbl else None self.rb=Abuf(rbl,encoding=self.encoding) if rbl else None self.sb=Abuf(sbl,encoding=self.encoding) if sbl else None self.vb=Abuf(vbl,encoding=self.encoding) if vbl else None self.ib=Abuf(ibl,encoding=self.encoding) if ibl else None self.nucid=0 # set after open(), if >0: assigned cluster nucid self.mfgen=None # generator set if multifetch if multifetch > 1: self.mfc=multifetch # number of records to fetch self.mfele=Mfele() self.mfhdr=Mfhdr() else: self.mfc=0 self.setadaid(thread)
[docs] def call(self, **cbfields): """ issue Call Adabas with the Adabas control block class variables :param cbfields: list of key value pairs that can be used to set the control block before the call >>> c1.call(cmd='L1', cid='ABCD', fnr=11, isn=12345) """ global totalCalls cb=self.cb for (key, val) in cbfields.items(): setattr(cb,key,val) if self.dbid==0 and cb.dbid!=0: self.dbid=cb.dbid # remember dbid if not yet done if self.password and cb.cmd[0] in ('AELNS'): # set pwd for read and upd commands cb.ad3=self.password if self.cipher and cb.cmd[0] in ('AELNS'): # set cipher code for read and upd commands cb.ad4=self.cipher if cb.typ==0x04: # physical call (default is 0x30) if self.nucid==0 and cb.pnucid!=0: self.nucid=cb.pnucid # remember dbid if not yet done cb.pdbid=self.dbid cb.pnucid=self.nucid cb.rsp=0 #o cb.dbid=0 else: cb.typ=0x30 # logical call (reset after call adaOS6.1) cb.dbid=self.dbid cb.pdbid=0 cb.pnucid=0 if self.thread: i = adalink.lnk_set_adabas_id(self.aidb) if defs.logopt & LOGBEFORE: self.logapa('Before Adabas call',before=1) # issue call i = adalink.adabas(self.acb, self.fb,self.rb,self.sb,self.vb,self.ib) totalCalls+=1 if i != 0 and self.cb.rsp==0: raise InterfaceError('Adabas call interface returned: %d' % i, self) if self.cb.rsp != 3: # prepare subcode data and error texts # or set compressed reclen / reclen if nativeByteOrder==HOBF: self.sub1=self.cb.ad2>>16 self.sub2=self.cb.ad2&0xFFFF else: self.sub2=self.cb.ad2>>16 self.sub1=self.cb.ad2&0xFFFF errtext=adaerror.rsptext(self.cb.rsp, self.sub1, self.sub2, cmd=self.cb.cmd, subcmd1=self.cb.op1, subcmd2=self.cb.op2), # print('logopt: %04X, logstr: %s' % (defs.logopt, defs.logstr)) # test if defs.logopt&LOGCMD or \ (defs.logopt&LOGRSP and \ (self.cb.rsp not in (0,2,3)) and \ not (self.cb.rsp == 64 and self.cb.cmd == 'CL')): self.logapa('After Adabas call') if self.noexceptions: # do not check response codes return if self.expected_responses: x = self.expected_responses.pop(0) if isinstance(x, tuple): xrsp,xsub = x else: xrsp, xsub = x, None # just a response code (with no subcode specified) if xsub == None: # only response code specified if defs.logopt&LOGCMD: with self.pmutex: adalog.debug('Checking for expected response %d'%xrsp) assert xrsp == self.cb.rsp, \ 'Unexpected response %d, expected response %d'%( self.cb.rsp, xrsp) else: if defs.logopt&LOGCMD: with self.pmutex: adalog.debug('Checking for expected response %d/%d'%(xrsp,xsub)) assert xrsp == self.cb.rsp and xsub == self.sub2, \ 'Unexpected response %d/subcode %d, expected response %d/%d'%( self.cb.rsp, self.sub2, xrsp, xsub) return if self.cb.rsp == 0: return elif self.cb.rsp == 2: # ignore DE truncation warning # self.cb.rsp = 0 return elif self.cb.rsp == 3: self.cb.rsp = 0 raise DataEnd("End of Data",self) elif self.cb.rsp > 0 and not \ (self.cb.rsp == 64 and self.cb.cmd == 'CL'): # do not raise if CL and rsp=64 raise DatabaseError(errtext,self)
[docs] def logapa(self,loghdr='',before=0): """ Logging of Adabas call parameters for Acb Options set in logopt determine which buffer to log :param before: set to true if logging before the adabas call """ with self.pmutex: rsp = self.cb.rsp if defs.logopt&(LOGCB) or (rsp and not before): adalog.debug(loghdr) self.showCB(before=before) hdr = 'Control Block '+repr(self.acb) if debug else 'Control Block' dump(self.acb, hdr, 'CB',log=adalog.debug) if defs.logopt & ~(LOGCB|LOGBEFORE): # any buffer logging at all? lopt = defs.logopt & ~(LOGCB|LOGBEFORE|LOGBUF) if defs.logopt & LOGBUF: # log buffers depending on command bf, bfa = cmdbufs.get(self.cb.cmd,(0,0)) if before: lopt |= bf # OR any direct log request with eligible buffers depending on command else: lopt |= bfa|(0 if defs.logopt&LOGBEFORE else bf) # include buffers sent to Adabas (were not logged before) if (lopt & LOGFB) and self.cb.fbl: hdr = 'Format Buffer '+repr(self.fb) if debug else 'Format Buffer' dump(self.fb, hdr, 'FB',log=adalog.debug) if (lopt & LOGRB) and self.cb.rbl: hdr = 'Record Buffer '+repr(self.rb) if debug else 'Record Buffer' dump(self.rb, hdr, 'RB',log=adalog.debug) if (lopt & LOGSB) and self.cb.sbl: dump(self.sb, 'Search Buffer', 'SB',log=adalog.debug) if (lopt & LOGVB) and self.cb.vbl: dump(self.vb, 'Value Buffer', 'VB',log=adalog.debug) if (lopt & LOGIB) and self.cb.ibl != 0: dump(self.ib, 'ISN Buffer', 'IB',log=adalog.debug)
[docs] def multifetch(self, dmap): """ Generator function :param dmap: datamap of record buffer - offset will be advanced :returns: tuple (ISN, datamap) Note: currently with ACB or ACBX with one RB/MB pair """ isn=None ad3=self.cb.ad3 # keep additions3 for repetitive call() ad4=self.cb.ad4 # keep additions4 for repetitive call() while 1: self.call() dmap.buffer=self.rb dmap.offset=0 if self.cb.op1 !='M': yield self.cb.isn, dmap continue # no multifetch running # initialize mulitifetch buffers if issubclass(self.__class__, Adabasx): mb=self.mb else: mb=self.ib # with ACB use ISN buffer self.mfhdr.buffer=mb n = self.mfhdr.elecount if n==0: raise DataEnd("End of Data in multifetch()",self) self.mfele.buffer=mb self.mfele.offset=4 for i in range(n): if self.mfele.rsp == 3: raise DataEnd("End of Data",self) elif self.mfele.rsp > 0: # any other response - no subcode provided in mfele raise DatabaseError( adaerror.rsptext(self.mfele.rsp, 0, 0), self) else: # rsp == 0 recl = self.mfele.reclen isn = self.mfele.isn if recl < 1: raise DataEnd("End of Data",self) else: yield isn, recl # return ISN and current record length ##### dmap.offset+=recl # advance in record buffer self.mfele.offset+=16 # advance in ISN buffer self.mfhdr.elecount=0 if self.cb.op2 in ('I','K'): # Read in ISN sequence self.cb.isn = isn+1 # next ISN elif self.cb.op2 == 'J': # Read descending in ISN sequence self.cb.isn = isn-1 # next ISN elif self.cb.cmd in ('L2','L5'): self.cb.isn = 0 # next ISN determined by nucleus isn=None self.cb.ad3=ad3 # Restore any Additions3 self.cb.ad4=ad4 # Restore any Additions4
# end of multifetch()
[docs] def multicall(self,nc=0): """Initialize Multi-call after Acb and buffers have been allocated :param nc: number of commands """ self.cb.cmd='MC' # Multi Call self.cb.isq=nc # number of commands self.cbs=[] # array of Acbs in one multicall buffer self.fboffs=[] self.rboffs=[] self.vbsoffs=[] self.sbsoffs=[] self.ibsoffs=[] self.fblu=0 # number of bytes used in buffer self.rblu=0 self.sblu=0 self.vblu=0 self.iblu=0 self.fbuh=None # buffer header structure self.rbuh=None self.sbuh=None self.vbuh=None self.ibuh=None # each buffer starts with a MC command buffer header with 2 short # integers (datamap Mcbuh): # - offset within buffer to global part used by all subcommands # - offset to array of (short integer) offsets to start of individual # parts one for each subcommand # for the record buffer the array of Adabas control blocks for the # number of subcommands follow if nc>0: # set up multiple Acbs within record buffer self.rbuh=Mcbuh(buffer=self.rb) self.rblu=self.rbuh.dmlen for i in range(nc): self.cbs.append(Acb(buffer=self.rb,offset=self.rblu, ebcdic=self.ebcdic, byteOrder=self.bo)) self.rblu += ACBLEN # updated used length of rb if self.rblu > self.rbl: raise ProgrammingError('Buffer length exceeded (multi-call)',self)
#o set up array of start offsets
[docs] def setoffs(self, btyp): """set start offsets within Multi-Call buffer """ pass
[docs] def setadaid(self, thread, adaidlev=ADAID_SL3): """Set Adabas communication id This method is called during intialization of Adabas/Adabasx object only if thread is not zero. :param thread: thread number """ self.thread=thread self.aidb=Abuf(ADAIDL) if sys.platform == 'zos': self.adaid=Adaid(buffer=self.aidb, ebcdic=1, byteOrder=NETWORKBO) else: self.adaid=Adaid(buffer=self.aidb) self.adaid.level=adaidlev self.adaid.size=ADAIDL if thread: self.adaid.node=socket.gethostname() try: # under mod_python getpass.getuser() cannot import pwd # set user from signon? self.adaid.user=getpass.getuser() # identification except: self.adaid.user='unkown' _pid = os.getpid() #if _pid > 0x7fff: # _pid -= 0x10000 # make negative self.adaid.pid = ( (_pid&0xffff) << 16) | thread # pid is integer # print('node=%s, user=%s, pid=%s' % \ # (self.adaid.node, self.adaid.user, self.adaid.pid)) ##c = Abuf(ADAIDL) ##adalink.lnk_get_adabas_id(ADAIDL,c);dump(c,'Adaid Original') #### i = adapy.setuser(self.node, self.user, self.pid) i = adalink.lnk_set_adabas_id(self.aidb) else: adalink.lnk_get_adabas_id(ADAIDL,self.aidb)
# self.adaid.dprint() # dump(self.aidb,'Adaid after setting')
[docs] def setcb(self, **cbfields): """ Set Adabas control block class variables in one call cbfields = list of key value pairs that can be used to set the control block >>> c1.setcb(cmd='L1', cid='ABCD', fnr=11, isn=12345) """ cb=self.cb for (key, val) in cbfields.items(): setattr(cb,key,val)
[docs] def showCB(self, before=0): """Print Adabas control block interpreted :param before: if not zero show CB fields relevant for before the adabas call (e.g. no response code, cmd time) """ cb=self.cb # Acb(buffer=self.acb) if before: adalog.debug('cmd=%s op1/2=%s/%s ad1=%s dbid=%d fnr=%d' % \ (cb.cmd, repr(cb.op1), repr(cb.op2), repr(cb.ad1), self.dbid, cb.fnr)) else: adalog.debug('cmd=%s op1/2=%s/%s ad1=%s dbid=%d fnr=%d cmdt=%6.3f ms rsp=%d' % \ (cb.cmd, repr(cb.op1), repr(cb.op2), repr(cb.ad1), self.dbid, cb.fnr, cb.cmdt*16./1000, cb.rsp)) adalog.debug('cid=%d isn=%d isl=%d isq=%d' % (cb.cidn, cb.isn, cb.isl, cb.isq)) adalog.debug('fbl=%d, rbl=%d, sbl=%d, vbl=%d, ibl=%d' % \ (cb.fbl, cb.rbl, cb.sbl, cb.vbl, cb.ibl)) adalog.debug('ad3=%s, ad4=%s, ad5=%s, pdbid=%d, pnucid=%d, pid=%04X' % \ (repr(cb.ad3), repr(cb.ad4), repr(cb.ad5), cb.pdbid, cb.pnucid, self.adaid.pid))
[docs] def open( self, mode=None, tna=0, tt=0, etid='', arc=None, acode=0, wcode=0, wcharset='', tz=''): """ Open a user session with a database. :var self.cb.dbid: must be set before calling this function :param acode: ECS code page number for client data encoding of A fields :param arc: architecture key is the sum of the following values - 1: low order byte first, default: high order byte first - 2: EBCDIC, default: ASCII - 4: VAX floating-point - 8: IEEE floating-point, default: IBM 370 float - default: arc=0 will use data format native to caller's machine :param etid: 8 bytes Adabas transaction user id :param mode: defines share mode of files, default None if mode is a string it may be a list of modes e.g. 'UPD=2,ACC=3' :param tna: time of non-activity :param tt: transaction time :param tz: Timezone name according to the Olson Timezone DB / pytz e.g. 'Europe/Paris' :param wcode: ECS code page number for client data encoding of W fields :param wcharset: IANA name for W field encoding (ADABAS OpenSystems V5.1, Mainframe V7.4 only recognizes 'UTF-8' for other encodings use wcode) """ self.cb.cmd='OP' self.cb.isl=tna self.cb.isq=tt self.cb.op1=' ' # option R restrict files if self.cb.op2 != 'E': self.cb.op2=' ' # leave option E read ET data self.cb.ad1=etid _l = [] if mode!=None: if isinstance(mode,str): _l.append(mode) else: _l.append(open_modes[mode][0]) # set open mode RB=UPD=. if arc != None: _l.append('ARC='+repr(arc)) if acode>0: _l.append('ACODE='+repr(acode)) if wcode>0: _l.append('WCODE='+repr(wcode)) if wcharset != '': _l.append("WCHARSET='"+wcharset+"'") if tz != '': _l.append("TZ='"+tz+"'") _rb = ','.join(_l) # make string with parts separated by comma self.rb.seek(0); self.rb.write_text(_rb+'.') self.call() # Adabas call self.updates = 0 # reset number of updates self.hexversion = self.cb.isq self.version = self.cb.isq>>24 self.release = self.cb.isq>>16 & 0xff self.smlevel = self.cb.isq>>8 & 0xff self.ptlevel = self.cb.isq & 0xff self.vrs = '.'.join(map( str,(self.version,self.release,self.smlevel,self.ptlevel))) self.dbarchit = self.cb.isl>>24 # # 0 1 2 4 self.opsys = self.cb.isl>>16 & 0xff # opsys=(Mf,VMS,OpenSystems,NPR) # OpenSystems = Unix, Windows self.nucid = self.cb.isl& 0xffff # nucid > 0 if cluster nucleus self.cb.isl=0 # reset isl and isq self.cb.isq=0
[docs] def nextcid(self): """Returns integer of next CID value for sequence operations (obsolete: use cidn=-1 to let Adabas automatically return)""" self.cidseq += 1 return self.cidseq
[docs] def checkpoint1(self,flush=0,id='UCP1'): """Write Checkpoint. :param flush: = 1 to start a bufferflush :param id: checkpoint id as 4 byte characterstring :var self.cb.dbid: must be set before calling this function """ self.cb.cmd='C1' self.cb.cid = str2ebc(id+4*' ') if flush: self.cb.op1 = 'F' self.call()
[docs] def checkpoint5(self): """Write User Checkpoint with data. :var self.cb.dbid: must be set before calling this function :var self.rb: The record buffer may hold checkpoint data """ self.cb.cmd='C5' self.call()
def _call_with_etdata(self,etdata): """Helper function to set <etdata> into record buffer in Adabas() or Adabasx() record buffers, adjust lengths and issue the Adabas call. With etdata == '' the E option is set too in order to write Session counters to ET record (with CL command) Called from: et() and close() """ # keep buffer size if issubclass(self.__class__, Adabasx): rbl = self.rabd.size else: rbl = self.cb.rbl if len(etdata) > rbl: raise DatabaseError('Length of etdata %d exceeds record buffer length %d' % (len(etdata),rbl), self) if len(etdata) > 2000: raise DatabaseError('Length of etdata %d exceeds 2000' % len(etdata), self) self.cb.op2='E' # option E read/write ET data if len(etdata): self.rb.value=etdata if issubclass(self.__class__, Adabasx): self.rabd.send=len(etdata) self.call() else: self.cb.rbl=len(etdata) self.call() self.cb.rbl=rbl # restore rbl self.cb.op2=' ' # reset E option
[docs] def close(self, etdata=None): """Close user session with database :param etdata: (optional) ET data to write with end of transaction requires *etid* being set with :meth:`Adabas.open` :var self.cb.dbid: must be set before calling this function """ self.cb.cmd='CL' self.cb.op1=' ' if etdata != None: self._call_with_etdata(etdata) else: self.cb.op2=' ' self.call() self.updates = 0 # reset number of updates
[docs] def et(self,etdata=''): """End Transaction with database. :param etdata: (optional) ET data to write with end of transaction requires *etid* being set with :meth:`Adabas.open` :var self.cb.dbid: must be set before calling this function .. note:: currently does not support Multifetch option Function supports both Adabas and Adabasx calls. """ self.cb.cmd='ET' self.cb.op1=' ' #self.cb.op3='H' # set option H to keep shared hold if etdata: # E option not set if etdata empty (unlike CL cmd) self._call_with_etdata(etdata) else: self.cb.op2=' ' self.call() self.updates = 0 # reset number of updates
[docs] def bt(self): """Backout Transaction with database dbid - must be set before calling this function Note: not ET-data supported, no Multifetch option """ self.cb.cmd='BT' self.cb.op1=' ' self.cb.op2=' ' # option E read ET data #self.cb.op3='H' # set option H to keep shared hold self.call() self.updates = 0 # reset number of updates
[docs] def find(self,saveisn=0,sort=''): """ Find records by selection :param saveisn: 1 saves ISNs on WORK for later processing under CID :param sort: 'FNF2F3' may specify up to 3 descriptors by which the selected records are sorted """ self.cb.cmd='S1' self.cb.op1=' ' self.cb.op2='I' # release ISN list for CID self.cb.ad1='' if saveisn: self.cb.op1='H' if sort != '': self.cb.ad1=sort self.cb.cmd='S2' #if 'acb' in dir(self): # self.cb.ibl=0 # don't read first record (old acb) self.call()
[docs] def first_unused(self, dbid=0, fnr=0): """ return first unused ISN from FCB This number can be used as an upper bound to the number of records loaded. Do not use this function on an expanded file. :returns: first unused ISN """ self.cb.op1='F' self.cb.cmd='L1' if dbid: self.cb.dbid=dbid if fnr: self.cb.fnr=fnr self.call() return self.cb.isn
[docs] def get(self, isn=0, hold=0, wait=0): """ get record with ISN=isn if hold is true: put record in hold (L4) if wait is true: wait if record is in hold """ self.cb.op1=' ' if hold: self.cb.cmd='L4' if not wait: self.cb.op1='R' else: self.cb.cmd='L1' if isn != 0: self.cb.isn=isn self.cb.op2=' ' self.call()
[docs] def getiseq(self, isn=None, hold=0, wait=0, dmap=None): """ get record with ISN in ISN sequence if hold is true: put record in hold (L4) if wait is true: wait if record is in hold if dmap set to a datamap and multifetch is set to > 1 in the Adabas class getiseq() returns the size of the record that is located in the datamap buffer at the offset returns tuple (ISN, record_length) Example with multifetch: >>> while c.getiseq(dmap=emp): ... emp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ self.cb.op2='I' if self.mfc>1 and dmap != None: # multifetch if self.mfgen==None: # first call if hold: self.cb.cmd='L4' if wait: self.cb.op1='M' else: self.cb.op1='O' # M+R combined else: self.cb.cmd='L1' if isn: self.cb.isn=isn self.mfgen=self.multifetch(dmap) return next(self.mfgen) else: if hold: self.cb.cmd='L4' if wait: self.cb.op1=' ' else: self.cb.op1='R' else: self.cb.cmd='L1' if isn: self.cb.isn=isn else: self.cb.isn+=1 self.cb.op1=' ' self.call() return self.cb.rbl # rbl is > 0
[docs] def getnext(self, hold=0, wait=0): """ get next record from ISN list after find() if hold is true: put record in hold (L4) if wait is true: wait if record is in hold """ self.cb.op1=' ' self.cb.op2='N' if hold: self.cb.cmd='L4' if not wait: self.cb.op1='R' else: self.cb.cmd='L1' self.call()
[docs] def histo(self, descriptor='', descending=0): """ Read Index by descriptor (deprecated) """ self.cb.op1=' ' # could be 'M' for multifetch if descriptor != '': self.cb.ad1=descriptor[:2]+' '*6 if descending: self.cb.op2='D' # descending else: self.cb.op2=' ' # ascending 'A' self.cb.isn=0 # reset ISN self.cb.cmd='L9' self.call()
[docs] def histogram(self,seq='',descending=0,dmap=None): """ Histogram sequence generator :param seq: histogram descriptor, e.g. seq='AA' :param descending: read descending if true (only with seq='descriptor' sequence) :param dmap: Datamap object :return: (value, quantity, lowest_ISN, PE_occurrcence) datamap object if *dmap* set to a Datamap and multifetch is set to > 1 in the Adabas class. The function returns the Datamap object that is located in the datamap buffer at the offset. It can be printed with .lprint() (line) or .dprint() (detail) Example with datamap: >>> for isn, xemp in c.histogram(seq='AE', dmap=emp): ... xemp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith Example without datamap: >>> emp.buffer = c.rb >>> for isn, _ in c.histogram(seq='AE'): ... emp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ if dmap and not dmap.buffer: dmap.buffer = self.rb dmap.offset = 0 self.cb.cmd='L9' self.cb.cidn = -1 # may not work (global cid counter in api.py?) self.cb.isn = 0 self.cb.op1 = ' ' self.cb.ad1 = seq[:2] # +' '*6 if descending: self.cb.op2 = 'D' # descending else: self.cb.op2 = 'A' # ascending if self.mfc>1 and dmap != None: # multifetch if self.mfgen==None: # first call self.cb.op1='M' mfgen=self.multifetch(dmap) while True: try: isn, rlen, isq = mfgen.next() yield isn, dmap, isq except DataEnd: break # returns with StopIteration else: # no multifetch # preserve for repeating calls # ad3 = self.cb.ad3 ad4 = self.cb.ad4 while True: try: self.call() if dmap: yield self.cb.isn, dmap, self.cb.isq else: yield self.cb.isn, 1, self.cb.isq # self.cb.ad3=ad3 # restore for next call self.cb.ad4=ad4 except DataEnd: break # returns with StopIteration
[docs] def hold(self, isn=0, wait=0): """ Put record in hold with ISN=isn (prevent other users from updating the record) if wait is true: wait if record is in hold """ self.cb.cmd='HI' self.cb.op1=' ' if not wait: self.cb.op1='R' # Return if record in hold if isn != 0: self.cb.isn=isn self.cb.op2=' ' self.call()
[docs] def insert(self, isn=0, fastde=None): "Alias for store()" return self.store(isn=isn, fastde=fastde)
[docs] def getopsys(self): """Return Operating System and Adabas Version Adabas open() call must have been done before :return: string containing Opesys and Adabas version of database nucleus Example: >>> c1.Adabas() >>> c1.cb.db=10025 >>> c1.open() >>> print(c1.getopsys()) Database 10025 is active, V8.3.1 arc=4, opsys=Mainframe (IBM/Siemens/Fujitsu), cluster nucid 54321, High-order-byte-first/EBCDIC/IBM-float """ opsysDict={0: 'Mainframe (IBM/Siemens/Fujitsu)', 1: 'VMS', 2: "Unix, Windows", 4: 'Entire System Server'} if self.opsys in opsysDict: s = opsysDict[self.opsys] else: s = '%d' % self.opsys if self.opsys != 4: return 'Database %5d is active, V%d.%d.%d.%d, arc=%d,'\ ' opsys=%s, cluster nucid %d' %\ (self.cb.dbid or self.dbid,self.version,self.release, self.smlevel,self.ptlevel,self.dbarchit, s, self.nucid) else: return 'Entire System %d is active, V%d.%d.%d.%d, arc=%d' %\ (self.cb.dbid or self.dbid,self.version,self.release, self.smlevel,self.ptlevel,self.dbarchit)
[docs] def printopsys(self): """Print Opsys and Adabas Version Adabas open() call must have been done before """ print(self.getopsys())
[docs] def rc(self,cid=None,op1='',op2=''): """Release Command ID as specified in Adabas Control Block CID field :param cid: optional parameter specifies the command id to be used for the RC :param op1: optional parameter specifies the value for command option 1 to be used for the RC (default = ' ') :param op2: optional parameter specifies the value for command option 2 to be used for the RC (default = ' ') :var self.cb.dbid: must be set before calling this function Note: Currently not specific to any resource (ISN List, sequence or format) """ if cid: self.cb.cid=cid self.cb.cmd='RC' self.cb.op1=op1 self.cb.op2=op2 self.call()
[docs] def readByIsn(self, getnext=0): """ Read Sequential by ISN getnext = 1 if getnext else 0 """ if getnext: self.cb.isn += 1 # next ISN # print( "readByISN() next ISN: %d" % self.isn) else: self.cb.cmd='RC' self.cb.op1=' ' self.cb.op2=' ' self.call() self.cb.cmd='L1' self.cb.op1=' ' self.cb.op2='I' self.call()
[docs] def readisnseq(self, hold=0, wait=0, dmap=None): """ Read in ascending ISN sequence generator (experimental) if hold is true: put record in hold (L4) if wait is true: wait if record is in hold if dmap set to a datamap and multifetch is set to > 1 in the Adabas class function returns the record that is located in the datamap buffer at the offset yields tuple (ISN, dmap) Example with multifetch: >>> for isn, xemp in c.readisnseq(dmap=emp): ... xemp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ #o check for dmap != None ??? if dmap: # and not dmap.buffer: dmap.buffer = self.rb dmap.offset = 0 #self.cb.cmd='RC' #self.cb.op1=' ' #self.cb.op2=' ' #self.call() # preserve for repeating calls # ad3 = self.cb.ad3 ad4 = self.cb.ad4 self.cb.op1=' ' self.cb.op2='I' if self.mfc>1: # multifetch # if self.mfgen==None: # first call self.cb.op1='M' if hold: self.cb.cmd='L4' if not wait: self.cb.op1='O' # M+R combined else: self.cb.cmd='L1' mfgen=self.multifetch(dmap) while True: try: isn, rlen = next(mfgen) yield isn, dmap #emp except DataEnd: break else: if hold: self.cb.cmd='L4' if not wait: self.cb.op1='R' else: self.cb.cmd='L1' while True: try: self.call() yield self.cb.isn, dmap # emp self.cb.isn+=1 # next ISN # self.cb.ad3=ad3 self.cb.ad4=ad4 # restore for next call except DataEnd: # print('DataEnd -> StopIteration') # raise StopIteration break
[docs] def readphys(self, hold=0, wait=0, dmap=None): """ Read in physical sequence generator (tested ok) :param hold: put record in hold (L5) if *hold* is true :param wait: if *wait* is true: wait if record is in hold :param dmap: Datamap object :return: datamap object if *dmap* set to a Datamap and multifetch is set to > 1 in the Adabas class. The function returns the Datamap object that is located in the datamap buffer at the offset. It can be printed with .lprint() (line) or .dprint() (detail) Example with multifetch: >>> for xemp in c.readphys(dmap=emp): ... xemp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ #o check for dmap != None ??? if dmap and not dmap.buffer: dmap.buffer = self.rb dmap.offset = 0 # print( 'readphys() init generator') # preserve for repeating calls # ad3 = self.cb.ad3 ad4 = self.cb.ad4 self.cb.op1=' ' self.cb.cidn = -1 # self.nextcid() if self.mfc>1: # multifetch # if self.mfgen==None: # first call self.cb.op1='M' if hold: self.cb.cmd='L5' if not wait: self.cb.op1='O' # M+R combined else: self.cb.cmd='L2' mfgen=self.multifetch(dmap) while True: try: isn, rlen = next(mfgen) yield isn, dmap except DataEnd: break # returns with StopIteration else: if hold: self.cb.cmd='L5' if not wait: self.cb.op1='R' else: self.cb.cmd='L2' while True: try: self.call() yield self.cb.isn, dmap # self.cb.ad3=ad3 # restore for next call self.cb.ad4=ad4 except DataEnd: break # returns with StopIteration
[docs] def readphysical(self, hold=0, wait=0, dmap=None): """ Read physical if hold is true: put record in hold (L4) if wait is true: wait if record is in hold if dmap set to a datamap and multifetch is set to > 1 in the Adabas class readphysical() returns the size of the record that is located in the datamap buffer at the offset Example with multifetch: >>> while c.readphysical(dmap=emp): ... emp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ if dmap and not dmap.buffer: dmap.buffer = self.rb dmap.offset = 0 if self.mfc>1 and dmap != None: # multifetch if self.mfgen==None: # first call self.cb.op1='M' if hold: self.cb.cmd='L5' if not wait: self.cb.op1='O' # M+R combined else: self.cb.cmd='L2' self.mfgen=self.multifetch(dmap) return next(self.mfgen) else: self.cb.op1=' ' if hold: self.cb.cmd='L5' if not wait: self.cb.op1='R' else: self.cb.cmd='L2' self.call() return self.cb.isn, 1 # no rbl with ACBX # self.cb.rbl # rbl is > 0
[docs] def read(self,seq='',descending=0,hold=0,wait=0,dmap=None,startisn=0,toisn=0): """ Read in sequence generator :param seq: read sequence * physical (default) or * by descriptor (e.g. seq='AA', search and value buffer must be set) * by ISN (seq='ISN' or seq='I') * get next (seq='NEXT' or seq='N') :param descending: read descending if true (only with seq='descriptor' sequence) :param hold: put record in hold if *hold* is true :param wait: if *wait* is true: wait if record is in hold :param dmap: Datamap object :return: datamap object if *dmap* set to a Datamap and multifetch is set to > 1 in the Adabas class. The function returns the Datamap object that is located in the datamap buffer at the offset. It can be printed with .lprint() (line) or .dprint() (detail) Example with datamap: >>> for isn, xemp in c.read(dmap=emp): ... xemp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith Example without datamap: >>> emp.buffer = c.rb >>> for isn, _ in c.read(seq='ISN'): ... emp.lprint() 12345678 John W Adkinson 12345699 Peter O Smith """ # toisn = 0 # not supported yet in Adabas if dmap and not dmap.buffer: dmap.buffer = self.rb dmap.offset = 0 if not seq.startswith('N'): self.cb.cidn = -1 self.cb.isn = startisn self.cb.op1=' ' if seq in ('I', 'ISN'): if hold: self.cb.cmd='L4' else: self.cb.cmd='L1' if descending: self.cb.op2='J' if toisn: if 0: # toisn > startisn: raise ProgrammingError('Read by ISN descending and startisn=%d < toisn=%d', (startisn, toisn)) self.cb.isq=toisn else: if toisn: if 0: # toisn < startisn: raise ProgrammingError('Read by ISN and startisn=%d > toisn=%d', (startisn, toisn)) self.cb.isq=toisn self.cb.op2='K' else: self.cb.op2='I' elif seq in ('N', 'NEXT'): if hold: self.cb.cmd='L4' else: self.cb.cmd='L1' self.cb.op2='N' elif seq: # some search criteria if hold: self.cb.cmd='L6' else: self.cb.cmd='L3' if len(seq) >= 2: self.cb.ad1=seq[:2]+' '*6 # set descriptor name if descending: self.cb.op2='D' # descending else: self.cb.op2='V' # ascending also 'A' else: # physical if hold: self.cb.cmd='L5' else: self.cb.cmd='L2' if self.mfc>1 and dmap != None: # multifetch if self.mfgen==None: # first call if hold and not wait: self.cb.op1='O' # M+R combined else: self.cb.op1='M' self.mfgen=self.multifetch(dmap) while True: try: isn, rlen = next(self.mfgen) dmap.dmlen = rlen yield isn, dmap except DataEnd: break # returns with StopIteration else: # no multifetch if hold and not wait: self.cb.op1='R' # preserve for repeating calls # ad3 = self.cb.ad3 ad4 = self.cb.ad4 while True: try: self.call() if dmap: dmap.dmlen = self.cb.ldec # decompr. reclen yield self.cb.isn, dmap else: yield self.cb.isn, 1 # self.cb.ad3=ad3 # restore for next call self.cb.ad4=ad4 if self.cb.op2 in ('I','K'): self.cb.isn+=1 # step up ISN for next call elif self.cb.op2=='J': isn = self.cb.isn self.cb.isn = 0 if isn < 1 else isn-1 # next lower ISN for next call elif not seq: # physical read self.cb.isn = 0 # reset ISN, next ISN found by nucleus except DataEnd: break # returns with StopIteration
[docs] def searchcrits(self, view, crit): """ free text form search mapped to search and value buffer entries :param view: - instance of Datamap with search fields attributed with fn=adabas_field_name - dict of Adabas shortname, length, format tuples accessed by longname or :param crit: selection criteria string (tokens separated by blank) e.g. " name = BELL and department = ADM1* " Comparison operators: < > <= >= = != and Adabas equivalent LT etc. with from-to written as field = ADKI* or field FROM 'A' TO 'B' Connecting operators: AND OR and Adabas defined D, O, R, Y, N Currently no parenthesis: i.e. criteria evaluated from left to right execept the Y """ coops = {'AND':'D', 'OR':'O', 'D':'D', 'O':'O', 'N':'N', 'R':'R', 'S':'S', 'Y':'Y'} ops={ '=': 'EQ', 'EQ': 'EQ', '!=': 'NE', 'NE': 'NE', '<': 'LT', 'LT': 'LT', '<=': 'LE', 'LE': 'LE', '>': 'GT', 'GT': 'GT', '>=': 'GE', 'GE': 'GE', 'FROM':'EQ','TO':'TO', 'THRU': 'TO'} op_inverse = dict(LE='GE', EQ='EQ', LT='GT') # 1 < b --> b > 1 cd = crit.split() if len(cd) < 3: raise InvalidSearchString('No search operator found: %s' % crit) while len(cd): b = cd[1].upper() op = ops.get(cd[1]) if op is None: raise InvalidSearchString('Invalid search operator found: %s' % cd[1]) if len(cd) < 3: raise InvalidSearchString('Missing search value: %s' % crit) a, b, c = cd.pop(0), cd.pop(0), cd.pop(0) if '0'<=a[0]<='9' or a[0] in ('"', "'"): # from-to 0 < salary < 9 val1, key = a, c op = op_inverse.get(op) if len(cd)<2: raise InvalidSearchString( 'Missing TO operator or value in FROM-TO expression : %s' % crit) d, val2 = cd.pop(0).upper(),cd.pop(0) op2 = ops.get(d) if op2 is None: raise InvalidSearchString('Invalid TO search operator found: %s' % d) else: val1, key, val2, op2 = c, a, '', '' if len(cd) > 1: # still more tokens? op2 = ops.get(cd[0].upper()) if op2 == 'TO': # adamf does not allow in from/to other VOP than EQ d, val2 = cd.pop(0),cd.pop(0) if isinstance(view,Datamap): fld = view.getfndef(key) else: fld = view.get(key) if not fld: raise InvalidSearchString("Missing Adabas field name for '%s' in Datamap %s" % (key,view.dmname)) fn, flen, ffrm = fld if val1[0] == val1[-1] and val1[0] in ('"', "'"): val1=val1[1:-1] if val2 and val2[0] == val2[-1] and val2[0] in ('"', "'"): val2=val2[1:-1] self.searchfield(fn,flen,val1,crit=op,ffrm=ffrm) #print( key, val1, flen, fn, op) #dump(self.sb) #dump(self.vb) if op2 and val2: self.sb.write_text(',S,') # S operator self.searchfield(fn,flen,val2,ffrm=ffrm) #print( key, val2, flen, fn, op2) #dump(self.sb) #dump(self.vb) if len(cd) > 3: e = cd.pop(0).upper() coop = coops.get(e) # connecting operator self.sb.write_text(',%s,' % coop) # AND or OR with previous self.sb.write_text('.')
[docs] def searchfield(self,fieldname,fieldlen,value,crit='',ffrm='',first=0,last=0): """ Insert a search value into the value buffer If last character of value contains '*' a from-to search is created Supported are: string and unicode values (with even field length in bytes):: if the search criterion is TO, LT or LE: the value is expanded with '\xff' to fieldlen if crit == 'TO': crit = '' if the search criterion is other than EQ, set the input param crit to GE, GT, LE, LT or NE. :parm ffrm: one of Adabas formats gets inserted to search buffer if numeric format (U,P,F,B or G): the input integer value is converted to appropriate Adabas value in value buffer :param first: if true it is the first search criterion in the buffers. Reposition search and value buffer to start positions :param last: if true it is the last search criterion in the buffers Terminate search buffer Note: Don't forget to terminate search buffer with '.' or set the parameter last=True """ if first: # first search criterion: reset search and value buffer position self.sb.pos=0 self.vb.pos=0 if ffrm == 'U': # Adabas Unpacked format? i = int(value) self.vb.write_text(fpack(i,ffrm,fieldlen)) self.sb.write_text('%s,%d,%s'%(fieldname,fieldlen,ffrm)) elif ffrm in ('P','F','B','G'): # other Adabas numeric format i = int(value) self.vb.write(fpack(i,ffrm,fieldlen)) # binary data self.sb.write_text('%s,%d,%s'%(fieldname,fieldlen,ffrm)) elif ffrm == 'W' and type(value)==type(u''): # unicode (UTF-16) #todo crit parameter eval if len(value)>0 and value[-1]=='*': # with wild card ln=len(value)-1 if (2*ln) >= fieldlen: ln=fieldlen//2-1 # limit to standard field length -1 value=value[:ln] # chop off wild card # avoid rsp-55/1: adabas/os truncates blanks internally and compares # u'abc ' == u'abc' < u'abc\u0000' # this is different to adabas/mf which does # u'abc\u0000' < u'abc' == u'abc ' uu=value # +u' ' ut=value+u'\uFA29' # highest 2 byte value in ICU collation # FFFF is the highest UTF-16 value (w/o surrogates) # dump(s1(uu.encode(UNICODE_INTERNAL))+s1(ut.encode(UNICODE_INTERNAL))) self.vb.write(s1(uu.encode(UNICODE_INTERNAL)) +s1(ut.encode(UNICODE_INTERNAL))) # utf_16 in native byteorder self.sb.write_text(fieldname+',0,S,'+fieldname+',0') else: ln=len(value) if ln>fieldlen//2: ln=fieldlen//2 uu=value+(fieldlen//2-ln)*u' ' # print( ln,value,fieldlen,fieldname) # dump(uu.encode(UNICODE_INTERNAL) ) self.vb.write(uu.encode(UNICODE_INTERNAL) ) self.sb.write_text(fieldname) else: # other encoding if len(value)>0 and value[-1] in (b'*','*'): # with wild card ln=len(value)-1 if ln>fieldlen-1: ln=fieldlen-1 # limit to standard field length -1 value=value[:ln] # chop off wild card self.vb.write_text(value) self.vb.write((fieldlen-ln) * b'\x00') self.vb.write_text(value) self.vb.write( (fieldlen-ln) * b'\xff') # self.sb.write_text(fieldname+',S,'+fieldname) self.sb.write_text(fieldname + ',%d,S,%s,%d' % (fieldlen,fieldname,fieldlen)) elif len(value)<fieldlen and crit in ('LE','LT','<','TO'): # with upper value ln=fieldlen-len(value) self.vb.write_text(value) self.vb.write(ln * b'\xff') self.sb.write_text(fieldname + ',%d' % fieldlen) if crit == 'TO': crit='' else: ln=len(value) if ln>fieldlen: ln=fieldlen self.vb.write(value[:ln]) self.vb.write_text((fieldlen-ln)*' ') self.sb.write_text(fieldname + ',%d' % fieldlen) if crit and crit != 'EQ': # omit EQ (=default) self.sb.write_text(','+crit) if last: # terminate search buffer self.sb.write_text('.')
[docs] def sortisns(self,saveisn=0,sort='ISN',cid='',descending=0): """Sort ISN list saved on WORK or given in ISN buffer :param cid: Command id of ISN list to be sorted. If cid is blank the ISN list is taken from the ISN buffer :param descending: if the ISN list is to be sorted by desciptors setting descending=1 will sort by descending values otherwise it will be ascending :param saveisn: 1 saves ISNs on WORK for later processing under CID :param sort: 'FNF2F3' may specify up to 3 descriptors by which the selected records are sorted. 'ISN' sorts the ISNs in ISN sequence (default) """ self.cb.cmd='S9' if descending: self.cb.op2='D' else: self.cb.op2=' ' if saveisn: self.cb.op1='H' else: self.cb.op1=' ' self.cb.op2='I' # release ISN list for CID given in cb.cid self.cb.ad1=sort self.call()
[docs] def store(self, isn=0, fastde=None): """ Store record into Adabas file :param isn: ISN if record to be stored under that ISN otherwise let Adabas assign ISN :param fastde: field name of descriptor that will be stored in ascending sequence. When specified the normal index blocks will be fully filled rather than half full due to not splitting the last block. This can half the number of new normal index blocks. """ self.cb.op1=' ' self.cb.op2=' ' if isn!=0: self.cb.cmd='N2' self.cb.isn=isn else: self.cb.cmd='N1' self.cb.isn=0 # Adabas assigns ISN if fastde: self.cb.op1='F' self.cb.ad1=fastde self.call() self.updates += 1 # count updates return self.cb.isn
[docs] def update(self, hold=0, isn=0, wait=0): """ Update record hold==1 put record into hold status before update wait_on_hold==0 return with RSP145 - ISN hold by other user isn = record to update """ self.cb.cmd='A1' if hold==0: self.cb.op2=' ' else: self.cb.op2='H' if wait==0: self.cb.op1='R' else: self.cb.op1=' ' if isn != 0: self.cb.isn=isn self.call() self.updates += 1 # count number of updates
[docs] def delete(self, isn=0, wait=0): """ delete record if wait is true: wait if record is in hold wait==0 return with RSP145 - ISN hold by other user """ self.cb.cmd='E1' if isn != 0: self.cb.isn=isn if wait==0: self.cb.op1='R' else: self.cb.op1=' ' self.cb.op2=' ' self.call() self.updates += 1 # count number of updates
#----------------------------------------------------------------------
[docs]class Adabasx(Adabas): "Set of Adabas classes to issue Adabas calls and write/read related buffers" def __init__(self, fbl=0, rbl=0, sbl=0, vbl=0, ibl=0, mbl=0, password='', pbl=0, pmutex=None, ubl=0, thread=0, multifetch=0, archit=None, noexceptions=0, cipher='', clientinfo=True ): global totalCalls gg = globals() if 'totalCalls' not in gg: totalCalls=0 self.sub1=0 self.sub2=0 self.cidseq=0 # automatic cid count (should be user related # OR use cidn=-1 for automatic assignment in Adabas as in read() self.cipher=cipher # cipher code self.clientinfo = clientinfo # Add self.expected_responses=[] # list of response/subcode tuples consumed on each call() self.noexceptions = noexceptions # do no fire exceptions after Adabas call with response code self.updates = 0 # reset number of updates (should be user session related) self.pmutex = pmutex if pmutex else defs.dummymutex self.password = password self.rdaarch = archit # architecture of adabas buffers self.encoding = 'latin1' # default buffer encoding unless architecture is EBCDIC self.ebcdic = 0 self.bo = NATIVEBO self.ecall = None if archit and (archit & RDAAEBC) and not (archit & RDAABSW): # mainframe native calls self.bo = NETWORKBO self.ebcdic = 1 self.encoding = 'cp037' # EBCDIC US (Latin1 characterset) self.rdaarch = RDAAEBC elif not archit and sys.platform == 'zos': self.ebcdic = 1 self.encoding = 'cp037' # EBCDIC US (Latin1 characterset) self.rdaarch = RDAAEBC self.acb = Abuf(ACBXLEN) self.cb = Acbx(buffer=self.acb, ebcdic=self.ebcdic, byteOrder=self.bo) self.abds=[] # empty list of ABDs self.bufs=[] # corresponding list of buffers self.rdaarch=archit # architecture of adabas buffers self.cb.ver=ACBXV2 # ACBX Version self.cb.len=ACBXLEN if fbl>0: self.fbabd = Abuf(ABDXL) self.fb = Abuf(fbl) self.fabd = Abdx(buffer=self.fbabd, ebcdic=self.ebcdic, byteOrder=self.bo) fd = self.fabd fd.len = ABDXL fd.ver = ABDXV2 fd.id = ABDXF # format buffer type fd.loc = ABDXIND # indirect fd.size = fbl fd.send = fbl fd.addr = ctypes.addressof(self.fb) self.abds.append(self.fbabd) self.bufs.append(self.fb) if rbl>0: self.rbabd = Abuf(ABDXL) self.rb = Abuf(rbl) self.rabd = Abdx(buffer=self.rbabd, ebcdic=self.ebcdic, byteOrder=self.bo) rd = self.rabd rd.len = ABDXL rd.ver = ABDXV2 rd.id = ABDXR # format buffer type rd.loc = ABDXIND # indirect rd.size = rbl rd.addr = ctypes.addressof(self.rb) self.abds.append(self.rbabd) self.bufs.append(self.rb) if sbl>0: self.sbabd = Abuf(ABDXL) self.sb = Abuf(sbl) self.sabd = Abdx(buffer=self.sbabd, ebcdic=self.ebcdic, byteOrder=self.bo) sd = self.sabd sd.len = ABDXL sd.ver = ABDXV2 sd.id = ABDXS # search buffer type sd.loc = ABDXIND # indirect sd.size = sbl sd.send = sbl sd.addr = ctypes.addressof(self.sb) self.abds.append(self.sbabd) self.bufs.append(self.sb) if vbl>0: self.vbabd = Abuf(ABDXL) self.vb = Abuf(vbl) self.vabd = Abdx(buffer=self.vbabd, ebcdic=self.ebcdic, byteOrder=self.bo) vd = self.vabd vd.len = ABDXL vd.ver = ABDXV2 vd.id = ABDXV # value buffer type vd.loc = ABDXIND # indirect vd.size = vbl vd.send = vbl vd.addr = ctypes.addressof(self.vb) self.abds.append(self.vbabd) self.bufs.append(self.vb) if ibl>0: self.ibabd = Abuf(ABDXL) self.ib = Abuf(ibl) self.iabd = Abdx(buffer=self.ibabd, ebcdic=self.ebcdic, byteOrder=self.bo) ia = self.iabd ia.len = ABDXL ia.ver = ABDXV2 ia.id = ABDXI # ISN buffer type ia.loc = ABDXIND # indirect ia.size = ibl ia.send = ibl ia.addr = ctypes.addressof(self.ib) self.abds.append(self.ibabd) self.bufs.append(self.ib) if mbl>0: self.mbabd = Abuf(ABDXL) self.mb = Abuf(mbl) self.mabd = Abdx(buffer=self.mbabd, ebcdic=self.ebcdic, byteOrder=self.bo) ma = self.mabd ma.len = ABDXL ma.ver = ABDXV2 ma.id = ABDXM # Multifetch buffer type ma.loc = ABDXIND # indirect ma.size = mbl ma.send = 0 ma.addr = ctypes.addressof(self.mb) self.abds.append(self.mbabd) self.bufs.append(self.mb) if clientinfo: # create performance buffer for client info self.cinfo = ClientInfoLuw(ebcdic=self.ebcdic, byteOrder=self.bo) cilen = self.cinfo.dmlen self.pb = Abuf(cilen) self.cinfo.buffer = self.pb self.setcinfo(self.cinfo) self.pbabd = Abuf(ABDXL) self.pabd = Abdx(buffer=self.pbabd, ebcdic=self.ebcdic, byteOrder=self.bo) pa = self.pabd pa.len = ABDXL pa.ver = ABDXV2 pa.id = ABDXP # Performace buffer type pa.loc = ABDXIND # indirect pa.size = cilen pa.send = cilen pa.addr = ctypes.addressof(self.pb) self.abds.append(self.pbabd) self.bufs.append(self.pb) else: self.cinfo = None # Create ABD array for later Adabasx call # This array may be zero in length if no ABDs exist pclass = ctypes.c_char_p * len(self.abds) self.abda = pclass() for i, abd in enumerate(self.abds): self.abda[i]=ctypes.cast(abd,ctypes.c_char_p) self.abdalen=len(self.abda) assert pbl==ubl==0,\ """Allocation of PB or UB buffers not yet implemented on instance creation: use addbuffer()""" self.mfgen=None # generator set if multifetch if multifetch > 1: self.mfc=multifetch # number of records to fetch self.mfele=Mfele() self.mfhdr=Mfhdr() else: self.mfc=0 self.setadaid(thread)
[docs] def addbuffer(self,type,buffer): """ create ABD for buffer and add abd and buffer of <type> to list of bufs and abds :param type: buffer type is one of ('F','R','M','S','V','I','P','U') :param size: if size > 0: reallocate buffer :returns abd: related ABD for further reference :raises ProgrammingError: when invalid buffer type given """ if type not in ('FRMSVIPU'): # format, record etc buffer raise ProgrammingError( 'Invalid Adabas buffer type %s. Should be one of "FRMSVIPU"' % type) if buffer == None: size = 0 addr = 0 else: size = len(buffer) addr = ctypes.addressof(buffer) addabd = Abuf(ABDXL) abd = Abdx(buffer=addabd, ebcdic=self.ebcdic, byteOrder=self.bo) abd.len = ABDXL abd.ver = ABDXV2 abd.id = type # format buffer type abd.loc = ABDXIND # indirect abd.size = size abd.addr = addr self.abds.append(addabd) # ctypes.cast(addabd, ctypes.c_char_p)) self.bufs.append(buffer) # Update ABD array for later Adabasx call pclass = ctypes.c_char_p * len(self.abds) self.abda = pclass() for i, abdi in enumerate(self.abds): self.abda[i]=ctypes.cast(abdi,ctypes.c_char_p) self.abdalen=len(self.abda) return Abdx(buffer=addabd, ebcdic=self.ebcdic, byteOrder=self.bo) # abd was cast to Cbuf
[docs] def newbuffer(self,type,size): """ resize existing buffer <type> if smaller :param type: is one of ('F','R','M','S','V','I','P','U') :param size: if size > 0: reallocate buffer :returns abd: related ABD for further reference """ for i,abdbuf in enumerate(self.abds): abd = Abdx(buffer=abdbuf, ebcdic=self.ebcdic, byteOrder=self.bo) if abd.id != type: continue if abd.size >= size: return abd else: buf = Abuf(size) if type == 'F': self.fb = buf # send size abd.send=size elif type == 'R': self.rb = buf elif type == 'S': self.sb = buf abd.send=size # send size elif type == 'V': self.vb = buf abd.send=size # send size elif type == 'I': self.ib = buf elif type == 'M': self.mb = buf else: raise ProgrammingError("Invalid Adabas call buffer type %s"%type,self) abd.size = size # set new size abd.addr = ctypes.addressof(buf) # set new buffer self.bufs[i] = buf # update list of buffers return abd raise ProgrammingError("Adabas call buffer type %s must exist"%type,self)
[docs] def call(self, **cbfields): """ issue Call Adabas with the Adabas control block class variables :param cbfields: list of key value pairs that can be used to set the control block before the call >>> c1.call(cmd='L1', cid='ABCD', fnr=11, isn=12345) """ global totalCalls cb=self.cb for (key, val) in cbfields.items(): setattr(cb,key,val) if cb.typ==0x04: # physical call if self.nucid==0 and cb.nid!=0: self.nucid=cb.nid # remember nucid if not yet done # cb.pdbid=cb.dbid # cb.pnucid=self.nucid if self.password and self.cb.cmd[0] in ('AELNS'): # set pwd for read and upd commands self.ad3=self.password if self.cipher and self.cb.cmd[0] in ('AELNS'): # set cipher code for read and upd commands self.ad4=self.cipher if self.thread: i = adalink.lnk_set_adabas_id(self.aidb) if defs.logopt & LOGBEFORE: self.logapa('Before Adabas call',before=1) totalCalls+=1 if self.cinfo: self.cinfo.callcnt = totalCalls # issue call i = adalink.adabasx(self.acb, self.abdalen, self.abda) if i != 0 and self.cb.rsp==0: raise InterfaceError('Adabas call interface returned: %d' % i, self) if defs.logopt&LOGCMD or \ (defs.logopt&LOGRSP and \ (self.cb.rsp not in (0,2,3)) and \ not (self.cb.rsp == 64 and self.cb.cmd == 'CL')): self.logapa('After Adabas call') if self.noexceptions: # do not check response codes return if self.expected_responses: x = self.expected_responses.pop(0) if isinstance(x, tuple): xrsp,xsub = x else: xrsp, xsub = x, None # just a response code (with no subcode specified) if xsub == None: # only response code specified if defs.logopt&LOGCMD: with self.pmutex: adalog.debug('Checking for expected response %d'%xrsp) assert xrsp == self.cb.rsp, \ 'Unexpected response %d, expected response %d'%( self.cb.rsp, xrsp) else: if defs.logopt&LOGCMD: with self.pmutex: adalog.debug('Checking for expected response %d/%d'%(xrsp,xsub)) assert xrsp == self.cb.rsp and xsub == self.cb.errc, \ 'Unexpected response %d/subcode %d, expected response %d/%d'%( self.cb.rsp, self.cb.errc, xrsp, xsub) return if self.cb.rsp > 0: if self.cb.rsp == 2: # ignore DE truncation warning self.cb.rsp = 0 pass elif self.cb.rsp == 3: raise DataEnd("End of Data",self) else: raise DatabaseError( adaerror.rsptext(self.cb.rsp, struct.unpack('=H',(self.cb.errbb+b'\x00\x00')[:2])[0],self.cb.errc, cmd=self.cb.cmd, subcmd1=self.cb.op1, subcmd2=self.cb.op2), self)
def setcinfo(self,ci): import os.path import datetime dt = datetime.datetime.now() # uname() returns named tuple from Python 3.3 # system, node, release, version, machine, processor if sys.hexversion < 0x03030100: class Uname(object): pass uname = Uname() uname.system, uname.node, uname.release,uname.version,\ uname.machine, uname.processor = platform.uname() else: uname = platform.uname() ci.eye = 'REVD' ci.ver = '00' ci.ver = '01' ci.appl = '' ci.prog = '' ci.uid = '' ci.stmt = '' ci.level = 0 ci.callcnt = 0 ci.execcnt = 0 ci.lib = '' ci.rpcclid = '' ci.rpcid = '' ci.rpcconvid = '' ci.secgroup = '' ci.eye2 = 'AUDL' ci.ver2 = '01' ci.date = dt.strftime('%Y%m%d') # 'YYYYMMDD' ci.time = dt.strftime('%H%M%S') # 'hhmmss' # ci.varspace = s1(username)+s1(programname)+s1(machinename)+s1(opsys)+ # s1(opsysver)+s1(hwname)+s1(hostname)+s1(tcpipaddr) varinfo = s1(getpass.getuser().encode(self.encoding))+ \ s1(os.path.basename(sys.executable).encode(self.encoding))+\ s1(uname.node.encode(self.encoding))+\ s1(uname.system.encode(self.encoding))+\ s1(uname.version.encode(self.encoding))+\ s1(uname.processor.encode(self.encoding))+\ s1(socket.gethostname().encode(self.encoding))+\ s1(getip().encode(self.encoding)) # print(len(varinfo), varinfo) # dump(varinfo) ci.varspace = varinfo
[docs] def logapa(self,loghdr='',before=0): """ Logging of Adabas call parameters for Acb Options set in logopt determine which buffer to log :param before: set to true if logging before the adabas call """ if before and defs.logopt&LOGBEFORE: with self.pmutex: if loghdr: adalog.debug('\n'+loghdr) else: adalog.debug('Before Adabasx call') self.showCB() if defs.logopt & LOGCB: hdr = 'Control Block Extended '+repr(self.acb) if debug else 'Control Block Extended' dump(self.acb, hdr, 'CB',log=adalog.debug) if defs.logopt & ~(LOGCB|LOGBEFORE): # any buffer logging at all? jf=jr=jm=js=jv=ji=jp=ju=0 lopt = defs.logopt & ~(LOGCB|LOGBEFORE|LOGBUF) if defs.logopt & LOGBUF: bf, _ = cmdbufs.get(self.cb.cmd,(0,0)) lopt |= bf # OR any direct log request with eligible buffers depending on command for i,abdb in enumerate(self.abds): # dump(abdb) abd=Abdx(buffer=abdb, ebcdic=self.ebcdic, byteOrder=self.bo) if lopt&LOGFB and abd.id=='F': jf+=1 hdr = 'FB ABD'+repr(abdb) if debug else 'FB%d ABD'%jf dump(abdb, hdr, 'FD%d'%jf,log=adalog.debug) dump(self.bufs[i], 'Format Buffer %d - %d/%d/%d - %08X' % ( jf, abd.size, abd.send, abd.recv, abd.addr), 'FB%d'%jf, log=adalog.debug) if lopt&LOGRB and abd.id=='R': jr+=1 hdr = 'RB%d ABD'%jr+repr(abdb) if debug else 'RB%d ABD'%jr dump(abdb, hdr, 'RD%d'%jr,log=adalog.debug) dump(self.bufs[i], 'Record Buffer %d - %d/%d/%d - %08X' % ( jr, abd.size, abd.send, abd.recv, abd.addr), 'RB%d'%jr, log=adalog.debug) if lopt&LOGMB and abd.id=='M': jm+=1 hdr = 'MB ABD'+repr(abdb) if debug else 'MB%1 ABD'%jm dump(abdb, hdr, 'MD%d'%jm,log=adalog.debug) dump(self.bufs[i], 'Multifetch Buffer %d - %d/%d/%d - %08X' % ( jm, abd.size, abd.send, abd.recv, abd.addr), 'MB%d'%jm, log=adalog.debug) if lopt&LOGSB and abd.id=='S': js+=1 dump(abdb, 'SB%d ABD'%js, 'SD%d'%js,log=adalog.debug) dump(self.bufs[i], 'Search Buffer %d - %d/%d/%d - %08X' % ( js, abd.size, abd.send, abd.recv, abd.addr), 'SB%d'%js, log=adalog.debug) if lopt&LOGVB and abd.id=='V': jv+=1 dump(abdb, 'VB%d ABD'%jv, 'VD%d'%jv,log=adalog.debug) dump(self.bufs[i], 'Value Buffer %d - %d/%d/%d - %08X' % ( jv, abd.size, abd.send, abd.recv, abd.addr), 'VB%d'%jv ,log=adalog.debug) if lopt&LOGIB and abd.id=='I': ji+=1 dump(abdb, 'IB%d ABD'%ji, 'ID%d'%ji,log=adalog.debug) dump(self.bufs[i], 'ISN Buffer %d - %d/%d/%d - %08X' % ( ji, abd.size, abd.send, abd.recv, abd.addr), 'IB%d'%jf, log=adalog.debug) if lopt&LOGPB and abd.id=='P': jp+=1 dump(abdb, 'PB%d ABD'%jp, 'PD%d'%jp,log=adalog.debug) dump(self.bufs[i], 'Performance Buffer %d - %d/%d/%d - %08X' % ( jp, abd.size, abd.send, abd.recv, abd.addr), 'PB%d'%jp, log=adalog.debug) if lopt&LOGUB and abd.id=='U': ju+=1 dump(abdb, 'UB%d ABD'%ju, 'UD%d'%ju,log=adalog.debug) dump(self.bufs[i], 'User Buffer %d - %d/%d/%d - %08X' % ( ju, abd.size, abd.send, abd.recv, abd.addr), 'UB%d'%ju, log=adalog.debug) elif defs.logopt&LOGCMD or \ (defs.logopt&LOGRSP and (self.cb.rsp not in (0,2,3)) and \ not (self.cb.rsp == 64 and self.cb.cmd == 'CL')): with self.pmutex: if loghdr: adalog.debug('\n'+loghdr) else: adalog.debug('After Adabasx call') self.showCB() if defs.logopt & LOGCB: dump(self.acb, 'Control Block Extended '+repr(self.acb), 'CB',log=adalog.debug) if defs.logopt & ~(LOGCB|LOGBEFORE): # any buffer logging at all? jf=jr=jm=js=jv=ji=jp=ju=0 lopt = defs.logopt & ~(LOGCB|LOGBEFORE|LOGBUF) if defs.logopt & LOGBUF: bf, bfa = cmdbufs.get(self.cb.cmd,(0,0)) lopt |= bfa # OR any direct log request with eligible buffers depending on command if not defs.logopt & LOGBEFORE: lopt |= bf # include buffers sent to Adabas for i,abdb in enumerate(self.abds): abd=Abdx(buffer=abdb, ebcdic=self.ebcdic, byteOrder=self.bo) if lopt&LOGFB and abd.id=='F': jf+=1 dump(self.bufs[i], 'Format Buffer %d - %d/%d/%d - %08X' % ( jf, abd.size, abd.send, abd.recv, abd.addr ), 'FB%d'%jf, log=adalog.debug) if lopt&LOGRB and abd.id=='R': jr+=1 dump(self.bufs[i], 'Record Buffer %d - %d/%d/%d - %08X' % ( jr, abd.size, abd.send, abd.recv, abd.addr), 'RB%d'%jr, log=adalog.debug) if lopt&LOGMB and abd.id=='M': jm+=1 dump(self.bufs[i], 'Multifetch Buffer %d - %d/%d/%d - %08X' % ( jm, abd.size, abd.send, abd.recv, abd.addr), 'MB%d'%jm, log=adalog.debug) if lopt&LOGSB and abd.id=='S': js+=1 dump(self.bufs[i], 'Search Buffer %d - %d/%d/%d - %08X' % ( js, abd.size, abd.send, abd.recv, abd.addr), 'SB%d'%js, log=adalog.debug) if lopt&LOGVB and abd.id=='V': jv+=1 dump(self.bufs[i], 'Value Buffer %d - %d/%d/%d - %08X' % ( jv, abd.size, abd.send, abd.recv, abd.addr), 'VB%d'%jv, log=adalog.debug) if lopt&LOGIB and abd.id=='I': ji+=1 dump(self.bufs[i], 'ISN Buffer %d - %d/%d/%d - %08X' % ( ji, abd.size, abd.send, abd.recv, abd.addr), 'IB%d'%jf, log=adalog.debug) if lopt&LOGPB and abd.id=='P': jp+=1 dump(self.bufs[i], 'Performance Buffer %d - %d/%d/%d - %08X' % ( jp, abd.size, abd.send, abd.recv, abd.addr), 'PB%d'%jp, log=adalog.debug) if lopt&LOGUB and abd.id=='U': ju+=1 dump(self.bufs[i], 'User Buffer %d - %d/%d/%d - %08X' % ( ju, abd.size, abd.send, abd.recv, abd.addr), 'FB%d'%ju, log=adalog.debug)
[docs] def showCB(self, before=0): """Print Adabas control block interpreted :param before: if not zero show CB fields relevant for before the adabas call (e.g. no response code, cmd time) """ cb=self.cb # Acb(buffer=self.acbx) if before: adalog.debug('cmd=%s op1/2/3=%s/%s/%s ad1=%s dbid=%d fnr=%d' % \ (cb.cmd, repr(cb.op1), repr(cb.op2), repr(cb.op3), repr(cb.ad1), self.dbid, cb.fnr)) else: adalog.debug('cmd=%s op1/2/3=%s/%s/%s ad1=%s dbid=%d fnr=%d cmdt=%6.3f ms rsp=%d' % \ (cb.cmd, repr(cb.op1), repr(cb.op2), repr(cb.op3), repr(cb.ad1), cb.dbid, cb.fnr, cb.cmdt*16./1000, cb.rsp)) adalog.debug('cid=%d isn=%d isl=%d isq=%d' % (cb.cidn, cb.isn, cb.isl, cb.isq)) adalog.debug('ad3=%s, ad4=%s, ad5=%s, pdbid=%d, pnucid=%d, pid=%04X' % \ (repr(cb.ad3), repr(cb.ad4), repr(cb.ad5), cb.pdbid, cb.pnucid, self.adaid.pid)) if cb.rsp!=0 and not before: respt = adaerror.rsptext(self.cb.rsp, struct.unpack('=H',(self.cb.errbb+b'\x00\x00')[:2])[0],self.cb.errc, cmd=self.cb.cmd, subcmd1=self.cb.op1, subcmd2=self.cb.op2) adalog.debug(respt)
# adalog.debug('\tError info: fn=%s subc=%d buf=%s#%d offs=%d ' % ( # repr(cbx.errb), cbx.errc, cbx.errd, cbx.errf, cbx.erra))
[docs] def open( self, mode=None, tna=0, tt=0, etid='', arc=None, acode=0, wcode=0, wcharset='', tz=''): """ Open a user session with a database. C{self.cb.dbid} must be set before calling this function :param acode: ECS code page number for client data encoding of A fields :param arc: architecture key is the sum of the following values: - 1: low order byte first, 2 - EBCDIC, - 4: VAX - 8: IEEE floating-point; - defaults: high order byte first, ASCII, IBM 370 float if arc=0 will use data format native to caller's machine :param etid: 8 bytes Adabas transaction user id :param mode: defines share mode of files, default None (in the future: list of sublist of mode, file elements) :param tna: time of non-activity :param tt: transaction time :param tz: Timezone name according to the Olson Timezone DB / pytz e.g. 'Europe/Paris' :param wcode: ECS code page number for client data encoding of W fields :param wcharset: IANA name for W field encoding (ADABAS OpenSystems V5.1, Mainframe only supports 'UTF-8') """ self.cb.cmd='OP' self.cb.isl=tna self.cb.isq=tt self.cb.op1=' ' # option R restrict files if self.cb.op2 != 'E': self.cb.op2=' ' # leave option E read ET data self.cb.ad1=etid _l = [] if mode!=None: _l.append(open_modes[mode][0]) # set open mode RB=UPD. if arc != None: _l.append('ARC='+repr(arc)) if acode>0: _l.append('ACODE='+repr(acode)) if wcode>0: _l.append('WCODE='+repr(wcode)) if wcharset != '': _l.append("WCHARSET='"+wcharset+"'") if tz != '': _l.append("TZ='"+tz+"'") _rb = ','.join(_l) # make string with parts separated by comma self.rb.seek(0); self.rb.write_text(_rb+'.') #if self.__class__.__name__=='Adabasx': self.rabd.send=len(_rb)+1 # set send size self.call() # Adabas call self.updates = 0 # reset number of updates self.version = self.cb.isq>>24 self.release = self.cb.isq>>16 & 0xff self.smlevel = self.cb.isq>>8 & 0xff self.ptlevel = self.cb.isq & 0xff self.dbarchit = self.cb.isl>>24 # # 0 1 2 4 self.opsys = self.cb.isl>>16 & 0xff # opsys=(Mf,VMS,OpenSystems,NPR) # OpenSystems = Unix, Windows self.nucid = self.cb.isl& 0xffff # nucid > 0 if cluster nucleus self.cb.isl=0 # reset isl and isq self.cb.isq=0
[docs]def space_calculation_AC( maxrec, blocksize=2544, rabnsize=3): """ Calculate space requirements for an Adabas file in in the Address Converter. :param maxrec: number of records :param blocksize: ASSO blocksize and :param rabnsize: DATA storage RABN size defined for database (3 or 4) >>> space_calculation_AC(5000,blocksize=2004) 8 >>> space_calculation_AC(5000,blocksize=2004,rabnsize=4) 10 >>> space_calculation_AC(10**9,rabnsize=4) 1572328 >>> space_calculation_AC(10**9,rabnsize=3) 1179246 """ isnsperblock = blocksize//rabnsize return (maxrec+isnsperblock-1) // isnsperblock
# Dict describing which buffers may be used for a given command # LOGSP marks cases where it depends also on the command options cmdbufs=\ { "A1":( LOGFB|LOGRB|LOGSP, LOGSP), "A4":( LOGFB|LOGRB|LOGSP, LOGSP), "A9":( 0, 0), "BT":( LOGSP, 0), "C1":( 0, 0), "C3":( LOGSP, 0), "C5":( LOGRB, 0), "CL":( LOGRB|LOGSP, 0), "E1":( LOGSP, 0), "E4":( LOGSP, 0), "ET":( LOGRB|LOGSP, 0), "HI":( 0, 0), "L1":( LOGFB, LOGRB|LOGSP), "L2":( LOGFB, LOGRB|LOGSP), "L3":( LOGFB|LOGSB|LOGVB|LOGSP, LOGRB|LOGSP), "L4":( LOGFB, LOGRB|LOGSP), "L5":( LOGFB, LOGRB|LOGSP), "L6":( LOGFB|LOGSB|LOGVB|LOGSP, LOGRB|LOGSP), "L9":( LOGFB|LOGSB|LOGVB|LOGSP, LOGRB|LOGSP), "LA":( 0, LOGRB), "LF":( 0, LOGRB), "MC":( LOGRB|LOGSP, LOGRB|LOGSP), "N1":( LOGFB|LOGRB, 0), "N2":( LOGFB|LOGRB, 0), "OP":( LOGRB|LOGSP, LOGRB|LOGSP), "PC":( LOGFB|LOGRB, LOGRB), "RC":( 0, 0), "RE":( 0, LOGRB), "RI":( 0, 0), "S1":( LOGFB|LOGSB|LOGVB, LOGRB|LOGIB|LOGSP), "S2":( LOGFB|LOGSB|LOGVB, LOGRB|LOGIB|LOGSP), "S4":( LOGFB|LOGSB|LOGVB, LOGRB|LOGIB|LOGSP), "S5":( 0, LOGIB), "S8":( 0, LOGIB|LOGSP), "S9":( LOGSP, LOGIB|LOGSP), "SP":( LOGRB, LOGRB), "U0":( 0, 0), "U1":( 0, LOGRB), "U2":( LOGRB, 0), "U3":( LOGRB, LOGRB), "V1":( LOGFB|LOGRB|LOGSB|LOGVB|LOGIB, LOGRB|LOGIB), "V2":( LOGFB|LOGRB|LOGSB|LOGVB|LOGIB, LOGFB|LOGRB|LOGSB|LOGVB|LOGIB), "V3":( LOGFB|LOGRB|LOGSB|LOGVB|LOGIB, LOGFB|LOGRB|LOGSB|LOGVB|LOGIB), "V4":( LOGFB|LOGRB|LOGSB|LOGVB|LOGIB, LOGRB|LOGVB|LOGIB), "X0":( 0, 0), "X1":( 0, LOGRB), "X2":( LOGFB, 0), "X3":( LOGFB, LOGRB), "YA":( LOGVB|LOGRB, LOGSP), "YB":( LOGVB|LOGIB, 0), "YD":( LOGVB, 0), "YE":( LOGVB, 0), "YF":( LOGRB|LOGVB|LOGIB, 0), "YP":( LOGVB, 0), "YR":( LOGVB, LOGRB), } # all fields of event info INFOFIELDS=( Uint2('etype'), Uint2('esubtype'), Uint2('dbid'), Uint2('nucid'), Int4('fnr'), Uint2('response'), Uint2('subcode'), Uint8('isn'), Uint8('etime', opt=T_STCK+T_GMT), Uint4('affected_tid', opt=T_HEX), # internal user id String('affected_jobname',8), Bytes('affected_cpuid',8, opt=T_HEX), String('affected_vmid',8, opt=T_EBCDIC), Uint4('affected_process', opt=T_HEX), String('affected_userid',8, opt=T_EBCDIC), String('affected_etid',8), String('affected_secuid',8), Uint4('holder_tid', opt=T_HEX), # internal user id String('holder_jobname',8), Bytes('holder_cpuid',8, opt=T_HEX), String('holder_vmid',8, opt=T_EBCDIC), Uint4('holder_process', opt=T_HEX), String('holder_userid',8, opt=T_EBCDIC), String('holder_etid',8), String('holder_secuid',8), ) INFOFB = 'AA,AB,AC,AD,AE,AF,AG,AH,AT,AQ,AI,AJ,AK,AL,AR,AM,AN,AO,AP.' INFOFB82 = 'AA,AB,AC,AD,AE,AF,AG,AH,AT,AI,AJ,AK,AL,AM,AN,AO,AP.' # all fields of event info (Adabas V8.2) INFOFIELDS82=( Uint2('etype'), Uint2('esubtype'), Uint2('dbid'), Uint2('nucid'), Int4('fnr'), Uint2('response'), Uint2('subcode'), Uint8('isn'), Uint8('etime',opt=T_STCK+T_GMT), String('affected_jobname',8), Bytes('affected_cpuid',8, opt=T_HEX), String('affected_vmid',8, opt=T_EBCDIC), Uint4('affected_process', opt=T_HEX), String('affected_userid',8, opt=T_EBCDIC), String('affected_etid',8), String('affected_secuid',8), String('holder_jobname',8), Bytes('holder_cpuid',8, opt=T_HEX), String('holder_vmid',8, opt=T_EBCDIC), Uint4('holder_process', opt=T_HEX), String('holder_userid',8, opt=T_EBCDIC), String('holder_etid',8), String('holder_secuid',8), ) INFOFB82 = 'AA,AB,AC,AD,AE,AF,AG,AH,AT,AI,AJ,AK,AL,AM,AN,AO,AP.'
[docs]class Infomap(Datamap): def __init__(self, *fields, **kw): Datamap.__init__(self, 'Infomap', *fields, **kw)
# selection of information fields INFOFIELDS2=( Uint2('type',colsize=4), Uint2('styp',colsize=4), Uint2('dbid'), Uint2('nucid'), Int4('fnr',colsize=5), Uint2('resp',colsize=4), Uint2('subc'), Uint8('isn',colsize=6), Uint8('etime', opt=T_STCK+T_GMT), Uint4('afftid', opt=T_HEX), #Bytes('affcpuid',8, opt=T_HEX), #String('affvmid',8, opt=T_EBCDIC), #Uint4('affproc', opt=T_HEX,repos=16), # skip 16 bytes cpu/vmid String('affuid',8, opt=T_EBCDIC,repos=20), String('affsecid',8), Uint4('hldtid', opt=T_HEX), #Bytes('hldcpuid',8, opt=T_HEX), #String('hldvmid',8, opt=T_EBCDIC), #Uint4('hldproc', opt=T_HEX, repos=16), String('hlduid',8, opt=T_EBCDIC, repos=20), String('hldsecid',8), ) INFOFB2 = 'AA,AB,AC,AD,AE,AF,AG,AH,AT,AQ,AJ,AL,AR,AN,AP.' INFOFB282 = 'AA,AB,AC,AD,AE,AF,AG,AH,AT,AJ,AL,AN,AP.' # selection of info fields ADA82 INFOFIELDS282=( Uint2('type',colsize=4), Uint2('styp',colsize=4), Uint2('dbid'), Uint2('nucid'), Int4('fnr',colsize=5), Uint2('resp',colsize=4), Uint2('subc'), Uint8('isn',colsize=6), Uint8('etime', opt=T_STCK+T_GMT), #Bytes('affcpuid',8, opt=T_HEX), #String('affvmid',8, opt=T_EBCDIC), Uint4('affproc', opt=T_HEX,repos=16), # skip 16 bytes cpu/vmid String('affuid',8, opt=T_EBCDIC), String('affsecid',8), #Bytes('hldcpuid',8, opt=T_HEX), #String('hldvmid',8, opt=T_EBCDIC), Uint4('hldproc', opt=T_HEX, repos=16), String('hlduid',8, opt=T_EBCDIC, repos=20), String('hldsecid',8), )
[docs]class Infomap2(Datamap): def __init__(self, *fields, **kw): Datamap.__init__(self, 'Infomap2', *fields, **kw)
""" '1,AA,2,B,FI' Event type =X'0001' for now '1,AB,2,B,FI' Event sub type =X'0000' for now '1,AC,2,B,FI' DBID '1,AD,2,B,FI' NUCID '1,AE,4,B,FI' File Number '1,AF,2,B,FI' Response Code =145 for now '1,AG,2,B,FI' Subcode =X'0000' for now '1,AH,8,B,FI' ISN '1,AT,8,B,FI' Time of Event (STCK value) '1,AQ,4,B,FI' Internal user ID (TID) of affected user '1,AI,8,A,FI' Job Name of affected user '1,AJ,28,A,FI,NV' User ID of affected user '1,AK,8,A,FI' ET ID of affected user V83 '1,AL,8,A,FI' SECUID of affected user '1,AR,4,B,FI' Internal user ID (TID) of holding user '1,AM,8,A,FI' Job Name of the user holding the record '1,AN,28,A,FI,NV' User ID of the user holding the record '1,AO,8,A,FI' ET ID of the user holding the record '1,AP,8,A,FI' SECUID of the user holding the record V83 """
[docs]def eventinfo(dbid, printinfo=1, detailed=1, version=''): """ return event information after response code initially only for response 145 :param dbid: dbid :param printinfo: when set eventinfo will be printed :param detailed: when set and printinfo is set eventinfo will be printed in detail :param version: '8.2' Adabas version 8.2 view '' - latest version (default) """ try: c2=Adabasx(fbl=100,rbl=200) c2.cb.dbid=dbid c2.cb.fnr = -4 # INFO buffer view c2.cb.cidn = -1 # get CID if version[0:3] == '8.2': c2.fb.seek(0); c2.fb.write_text(INFOFB82) fields,fields2=INFOFIELDS82,INFOFIELDS282 else: c2.fb.seek(0); c2.fb.write_text(INFOFB) fields,fields2=INFOFIELDS,INFOFIELDS2 if detailed: ev=Infomap(*fields) # detailed view does not fit into line -> no header print else: ev=Infomap2(*fields2) if printinfo: ev.lprint(header=1) # normal user has only one record # and getnext returns rsp-22 #while c2.readphysical(dmap=infomap2): # normal user gets only last event # infomap2.lprint() if c2.readphysical(dmap=ev): if not printinfo: return ev else: if detailed: ev.dprint() else: ev.lprint() return None except DatabaseError as e: print('Error obtaining eventinfo') print(e.value) dump(e.apa.acb, header='Control Block') return None
def refreshfile(dbid, fnr): rf=Adabas(rbl=100) rf.dbid=dbid rf.cb.fnr=fnr rf.delete() # isn=0 is refresh file # Copyright 2004-ThisYear Software AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.