KirbyBase é um banco de dados simples - escrito totalmente em Python - e que usa um arquivo texto único para armazenar as informações. Algumas de suas principais características são:
O KirbyBase atende aos casos em que ficamos entre o arquivo texto puro e pequenos banco de dados - como o SQLite ( http://www.sqlite.org ) e o Gadfly ( http://gadfly.sourceforge.net ) - para armazenar nossas informações. Dentro desse escopo ele é realmente útil e funcional.
Se você achou utilidade para o KirbyBase, está aplicando ele em algum projeto, ele te salvou o emprego, por favor, mande-me um e-mail informando. Essa é a maior recompensa e motivação para que eu continue meu trabalho no KirbyBase.
Para usar o KirbyBase o primeiro passo é importar o módulo:
Feito isso, partimos para criar uma instância*:from kirbybase import
KirbyBase, KBError
db = KirbyBase()
------------------
* Essa instância, sem referência a ip, porta e tal, é usada quando o arquivo
banco de dados está no disco rígido, o mesmo disco que a aplicação que vai usá-lo.
Por padrão, a instância é local e usa o mesmo espaço de memória que a sua aplicação. Para especificar uma conexão cliente/servidor, ficaria algo mais ou menos assim: db = KirbyBase('client', '192.168.0.10', '44444')
- Lembra que eu disse que só era necessário alterar uma linha ?Logicamente, você substituirá o endereço IP e a porta pelos valores do seu servidor e pela porta em que o KirbyBase está rodando.
Para criar uma nova tabela, você especifica o nome do arquivo local - incluindo o caminho ( path ) - que servirá para armazenar a tabela, e uma lista contendo os nomes dos campos e seus respectivos tipos. Por exemplo, para criar uma tabela com informações de aviões da Segunda Guerra Mundial:
result = db.create('plane.tbl', ['name:str', 'country:str', 'speed:int', 'range:int',
'began_service:datetime.date'])
Se você precisar armazenar a tabela em um diretório diferente do que o corrente, ponha-o em um diretório chamado db dentro do diretório corrente, por exemplo ( incluir o caminho do arquivo da tabela, serve para todos os métodos do KirbyBase ):
result = db.create('./db/plane.tbl', ['name:str', 'country:str', 'speed:int',
'range:int','began_service:datetime.date'])
Observe que na lista de campos você separa o nome do campo do tipo do campo através dos dois pontos ( ":" ). O KirbyBase criará automaticamente uma chave primária chamada 'recno' para cada tabela ( é o Id ao qual me referi acima ). Esse campo, esse Id, irá se auto-incrementar sempre que um novo dado for adicionado. Você pode usá-lo nos métodos insert, delete e update, mas você não pode removê-lo sem contudo remover o registro.
Os tipos de campo permitidos no KirbyBase atualmente são str, int, float, bool, datetime.date e datetime.datetime. Para ter compatibilidade entre as versões do KirbyBase para Python e para Ruby, você pode usar os seguintes tipos: String, Integer, Float, Boolean, Date, e DateTime. Use esses tipos se sua intenção for usar suas tabelas tanto no Python quanto no Ruby, usando suas respectivas versões do KirbyBase.
A chamada ao método create mencionada anteriormente retornará True, se a tabela for criada com sucesso.
Para inserir dados na tabela, use o método insert:
recno = db.insert('plane.tbl', ['P-51', 'USA', 403, 1201, date.datetime(1943,05,27)])
O tamanho da lista que representa os dados deve coincidir com o da lista de campos ( exclui-se nesse caso o campo "recno", pois ele é uma chave primária que define seu próprio valor ). E ainda, os tipos também devem coincidir. No exemplo acima, pôr '403', no lugar de 403, resultaria num erro.
Você também pode usar um dicionário para definir o valor dos campos. Reescrevendo o exemplo acima para usar um dicionários, ao invés de duas listas, ficaria da seguinte maneira:
recno = db.insert('plane.tbl', {'name': 'P-51', 'country':'USA','speed': 403, 'range':1201,
'began_service':date.datetime(1943,05,27)})
Você também pode usar uma instância de objeto para definir os valores dos campos. Para cada campo do dado a ser inserido, deve corresponder um atributo na instância do objeto, esse atributo - logicamente - com o valor que você quer adicionar no campo da tabela. Reescrevendo o exemplo acima para usar um objeto ao invés de um dicionário, ficaria dessa maneira:
class Record(object): pass
rec = Record()
rec.name = 'P-51'
rec.country = 'USA'
rec.speed = 403
rec.range = 1201
rec.began_service = datetime.date(1943,05,27)
recno = db.insert('plane.tbl', rec)
A chamada ao método insert retornará a Id ( "recno" ) recebida pelo novo dado inserido na tabela. Esse é um número gerado automaticamente pelo KirbyBase. Esse número nunca mudará, ele sempre será referente ao dado recém adicionado, podendo ser usado posteriormente como referência.
Para inserir vários dados de uma só vez na tabela você deve usar o método insertBatch:
recsToInsert =[
['P-51', 'USA', 403, 1201,
date.datetime(1943,05,27)],
['P-47', 'USA',
379, 805, date.datetime(1942,01,11)]
]
recnoList = db.insertBatch('plane.tbl', recsToInsert)
Esse método aceita uma lista de dicionários ou uma lista de listas ou uma lista de ambos, sendo que cada elemento corresponde a um novo registro a ser inserido na tabela.
O método insertBatch retorna uma lista contendo os Id's obtidos por cada novo registro criado pelo KirbyBase.
A sintaxe que você deve usar para selecionar registros em tabelas ( query's ) é a mesma para os métodos insert, delete e update. Portanto, vou explicar primeiramente como formular essas query's e depois passamos a explicar cada método separadamente.
Para todos os tipos, exceto string e booleans, você pode construir uma query usando operadores de comparação da própria linguagem Python. Para campos string, existem duas maneiras de voce fazer a sua query: (1) usando texto, isto é, fazendo uma busca exata por determinada palavra, frase ou caracter (2) usando uma expressão regular, e com esse recurso conseguir fazer buscas mais "amplas". Para campos do tipo boolean, você usa a busca por texto exato ( claro, na prática você só buscará ou por True ou por False, nada além disso no que diz referência a esses campos ).
Aqui está uma tabela com o tipo de campo e as possíveis maneiras de fazer querys para eles:
| Tipo | Sintaxe permitida |
|---|---|
| int | ==, =>, <=, >, <, <>, != |
| float | ==, =>, <=, >, <, <>, != |
| date | ==, =>, <=, >, <, <>, != |
| datetime | ==, =>, <=, >, <, <>, != |
| str | busca exata (i.e. 'John'), expressão regular (i.e. '^John$') |
| bool | True, False |
Primeiramente, vamos discutir o uso dos operadores simples de comparação da linguagem Python. Isso inclui: ==, =>, <=, >, <, <> e !=. Em cada um dos métodos insert, update e delete você simplesmente põe o operador de comparação entre aspas, juntamente com o valor a ser comparado (i.e. '>300'). Em vez de ficar fazendo hard-code e pôr números a cada comparação, você pode usar o operador de formatação de Python para poder usar variáveis no seu código (i.e. '>%d' % myvar). Essa sintaxe funciona para todos os tipos de dados, exceto strings e booleans.
Para campos do tipo string, você pode selecionar entradas simplesmente indicando valores aos quais elas devem coincidir exatamente . Por exemplo, se você quisesse selecionar todos as entradas onde o primeiro nome é "John", você simplesmente poderia usar o valor 'John'. Isso selecionaria todos "Johns", porém nenhum dos "Johnnys". Esse é o comportamento padrão para seleções em campos string. Nota: Essa é uma mudançã da versão 1.7. Para versões anteriores a 1.7 o comportamento padrão era selecionar entradas usando expressões regulares, o que será descrito adiante.
Existe uma outra maneira de selecionar entradas do tipo string: usando as expressões regulares de Python. Qualquer expressão regular válida servirá. Logo, se , por exemplo, eu quiser buscar todas as entradas em que o nome começa por "John", eu posso expressar a busca da seguinte maneira: '^John'. Isto retornaria todas as entradas onde o nome completo começa com "John", ex: i.e. "John Jones", "Johnny Doe", mas, por causa do símbolo ^, não retornaria algo como: "Jack Johnson". Para incluir também essa entrada no resultado, você teria que mudar a expressão apenas para 'John'. Para habilitar o uso de expressões regulares em suas buscas você deverá passar o seguinte dado na chamada da função: useRegExp=True.
Você deve estar se perguntando, "Se está correto usar na seleção de um campo int ou date '==45', porque eu não posso fazer algo como '==John' para selecionar entradas string? Porque para seleções envolvendo string eu devo usar 'John' (ou '^John$' se useRegExp=True)? Bem, a resposta é que para todo tipo de campo exceto campos-string, o KirbyBase sabe distinguir o que é um operador do que é o valor. Em outras palavras, tome - por exemplo - a expressão '==45'. Se ela estiver sendo usando para um campo inteiro, KirbyBase saberá que o '==' é o operador que significa "é igual" e que 45 é o valor a ser usado nas comparações. Mas e se a mesma expressão '==45' é usada para um campo string? O KirbyBase ficará sem saber se o usuário está buscando entradas que tenham tal valor igual a '45' ou igual a '==45'. Aí está o porque da não possibilidade de usar operadores para strings. Para ser honesto, eu ainda não parei para trabalhar nisso. Porém tenho certeza de que se isso virar uma necessidade, acharei uma solução elegante para o problema.
Para campos do tipo bool, você pode especificar o critério de seleção simplesmente comparando o campo com as palavras reservadas ( built-in ) de Python: True ou False.
Você pode ter múltiplos critérios de seleção numa query. Por exemplo, para selecionar todos os registros onde temos no primeiro nome "John" e que tenha o salário maior que $50,000, você poderia especifica 'John' e '>50000' na sua expressão de query. Isso será explicado mais claramente nos exemplos do método select, situados mais abaixo.
Você pode selecionar múltiplos registros simplesmente pondo uma lista dos vários Id's a serem retornados. Você verá um exemplo disso na descrição do método select.
Finalmente, existe uma maneira especial de selecionar todos os registros. Na expressão da query, se você usar como campo o 'recno' e como critério de seleção '*', KirbyBase irá retornar todos os registros contidos na tabela.
Agora que entendemos como funciona a seleção/busca de registros, podemos dar uma olhada mais profunda nos métodos select, update e delete.
O método select nos permite buscar por registros em que o campo X atente ao critério Y, podem ser vários campos e vários critérios, basta que o número de campos e o número de critérios coincida. Você ainda pode selecionar quais campos dos registros devem ser retornados, ordem como você quer que os campos do resultado sejam listados, e o formato dos registros no resultado ( i.e. lista, dicionário ou objeto ).
result = db.select('plane.tbl', ['country'], ['USA'])
Essa chamada retorna todos os registros da tabela que tenham no campo "country" o valor 'USA'. Já que o padrão é não usar expressões regulares, o select irá buscar por registros em que o campo "country" tenha o valor exato: 'USA'. Como nós não especificamos quais campos devem ser retornados no resultado do select, todos os campos do registro serão retornados. E ainda, como nós não especificamos uma ordenação dos resultados, o resultado não terá ordem nenhuma, virá como está na tabela do banco de dados.
Agora, se nós quisessemos retornar todos os registros que tenham como país ou 'EUA' ou "United States", como fariamos? Uma maneira seria especificar explicitamente ambos na nossa query:
result = db.select('plane.tbl', ['country','country'], '['USA','United States'])
Uma outra maneira seria usar expressões regulares:
result = db.select('plane.tbl', ['country'], ['US|United States'], useRegExp=True)
Observe que deve-se passar a variável useRegExp como o valor setado para True, para, aí sim, o KirbyBase poder fazer buscas usando expressões regulares.
Agora vamos exemplificar o uso de operadores de comparação no select. Para selecionar todos os aviões que tem velocidade entre 300 mph e 400 mph, fariamos algo como:
low_speed = 300
high_speed = 400
result = db.select('plane.tbl', ['speed', 'speed'],['>%d' % low_speed, '<%d' % high_speed])
Deve-se notar que para selecionar os dados entre um intervalo, eu apenas utilizei o mesmo campo duas vezes, comparando-o com o menor e o maior valor. Outra coisa que deve ser percebida é que você pode usar o operador Python de substituição de variáveis ( % ).
E como buscar por aviões que sejam rápidos e tenham longo alcance ( "range" ) ?
result = db.select('plane.tbl', ['speed', 'range'], ['>350','>900'])
Você pode, claro, combinar comparações string e não-string em um mesmo comando select. Isso torna possível, por exemplo, selecionar todos os aviões bombardeiros que tenham velocidade acima de 300mph:
result = db.select('plane.tbl', ['plane_type', 'speed'], ['^Bomber', '>300'], useRegExp=True)
Obeserve que no valor usado para comparar com o "plane_type" ( "tipo de avião" ) nós colocamos o símbolo '^', que indica que nós só queremos tipos de avião que comecem com a palavra "Bomber". Isso garante que não tenhamos registros de avião do tipo 'Fighter-Bomber' no resultado. O símbolo ^ é um metacaractere usada na expressão regular para indicar início de frase, isto é, "^Bomber" retornará todos campos "plane_type" em que o valor começar por Bomber, pode ser: Bomber-blue, Bomber-red, mas não pode ser BBBBomber. Toda a sintaxe de expressão regular funciona no KirbyBase.
Mais informações sobre expressões em geral e sobre o uso de expressões regulares em Python, aqui.
Selecionando com campos do tipo date e datetime
Vamos selecionar registro usando um campo do tipo date. Exemplo, vamos selecionar todos os aviões que entraram em serviço antes de 1940:
result = db.select('plane.tbl', ['began_service'], ['<%s' % datetime.date(1940, 1, 1)])
Obeserve que você usa %s para efetuar as comparações de data. Isso acontece porque o KirbyBase não lida com o date como um objeto, e sim, uma string. Datas são convertidas para string. A razão principal disso é a performance do banco de dados e além do mais, os resultados obtidos são os mesmos, seja usando objetos date, seja usando strings.
Vamos ver um exemplo de como selecionar registros usando campos do tipo bool. O código-exemplo a seguir seleciona todos aviões que ainda estão voando:
result = db.select('plane.tbl', ['still_flying'], [True])
Para selecionar um único registro, usando o seu id ( "recno" ):
result = db.select('plane.tbl', ['recno'], [245])
Para selecionar múltiplos registros usando seus Ids, "recno"´s:
result = db.select('plane.tbl', ['recno'],[2,5,7])
result = db.select('plane.tbl', ['recno'],['*'])
O método select retorna uma lista de registros. Cada resultado contido nessa lista pode ser do tipo: lista, dicionário, objeto ou uma string formatada para ser escrita na tela. O tipo padrão é a lista. Por exemplo, para selecionar todos aviões, porém retornar cada registro como um dicionário, fazemos:
result = db.select('plane.tbl', ['recno'], ['*'],returnType='dict')
for record in result:
print record['name']
Agora os registros retornados são objetos:
result = db.select('plane.tbl', ['recno'], ['*'],returnType='object')
for record in result:
print record.name
Se você especificar 'report' na variável returnType, o resultado virá na forma de tabela, útil para impressão. Mais um exemplo, agora usando essa propriedade:
print db.select('plane.tbl', ['recno'], ['*'],['recno','name','country', 'role'],returnType='report')
Fará o resultado do select ser algo como:
recno | name | country | role
---------------------------------------------
1 | P-51 | USA | Fighter
2 | P-47 | USA | Fighter
3 | B-17 | USA | Bomber
4 | Typhoon | England | Fighter-Bomber
5 | Sptitfire | England | Fighter
6 | Oscar | Japan | Fighter
7 | ME-109 | Germany | Fighter
8 | JU-88 | Germany | Bomber
10 | Zero | Japan | Fighter
Você pode definir quantos registros são impressos antes de um "formfeed" (\f) ser emitido e se linhas pontilhadas são impresssas entre os registros, basta suprir um lista de dois elementos para a variável rptSettings. O primeiro elemento da lista é um número inteiro que indica quantos registros devem ser impressos antes do "formfeed". O padrão é "0" ( zero ), o que indica a não inserção de "formfeeds". O segundo é um boolean ( True ou False ). Se for passado True, linhas pontilhadas serão impressas entre os registros, se falso ( comportamento padrão ) não serão impressas essas linhas.
Por padrão, o resultado do select terá todos os campos. Mas caso você queira que apenas alguns campos sejam retornados nesse resultado, você pode cria uma lista "filtro". Por exemplo, para selecionar todos os aviões que são do "country" "USA", retornando apenas o nome ( "name" ) e a velocidade ( "speed" ) dos registros encontrados:
result = db.select('plane.tbl', ['country'], ['USA'], ['name','speed'])
Você também talvez queira ordenar seus resultados de acordo com algum campo. O argumento sortFields, que faz essa ordenação, deve ser uma lista com nomes válidos de campos. O resultado será ordenado de acordo com a ordem dos campos na lista passada como argumento. Se nós especificassemos o sortFields como ['country','role','name'] ordenará o resultado de modo que os aviões de cada país serão agrupados, onde os aviões Alemãos ( Germany ) vindo antes dos Americanos ( USA´s ), e dentro de cada grupo de país, aviões "bombers" serão agrupados antes dos aviões tipo "fighters" e dentro do grupo de aviões "bombers" os registros estarão listados por ordem alfabética. Se você passar uma lista que limita os campos retornados ( parágrafo acima ), a lista de campos listados no sortFields deve pertencer a lista filtro, pois você não pode ordenar algo que não é retornado.
Por exemplo, podemos ordenar os aviões por grupos de países sendo que dentro de cada grupo eles serão ordenados pelo nome:
result = db.select('plane.tbl', ['recno'], ['*'], sortFields=['country','name'])
Se um campo de ordenação é passado, o ordenamento será por padrão ascendente ( do menor para o maior, de a para z ); para ordenar de modo descendente você deve passar uma lista para a variável sortDesc. Nessa lista vão os nomes dos campos que você desejaria ordenar de modo descedente. Sendo que cada integrante dessa lista também deve pertencer a lista passada para a variável sortFields. Essa condição é lógica pois você não pode dizer que quer ordenar algo de modo descendente se você sequer disse que queria ordená-la. Por exemplo, você quer ordenar os resultados por grupos de função ("role") dos aviões, porém deseja que os aviões mais rápidos ("speed") apareçam em primeiro nesses grupos:
result = db.select('plane.tbl', ['recno'], ['*'],sortField=['role', 'speed'],sortDesc=['speed'])
Para atualizar uma tabela, você precisar definir um critério para seleção dos registros a serem atualizados. Para atualizar um único registro basta você usar o 'recno' dele. Desse modo se procurará exatamente por esse registro, atualizando-se assim no mínimo um registro apenas. Um exemplo disso seria:
result = db.update('plane.tbl', ['recno'], [54], [405], ['speed'])
Nesse exemplos estamos atualizando o registro de 'recno' igual a 54. Nós estamos mudando a velocidade desse registro para 405 mph. Buscas exatas, como as por 'recno', são muito mais rápidas, porque o KirbyBase devolverá o resultado assim que o registro for encontrado, não será preciso buscar por toda a tabela.
Você também pode definir critérios de seleção para múltiplos campos usando expressões regulares ( para campos do tipo string ) e operadores de comparação para os outros tipos ( exceto: string e bool ). Você pode fazer diversas combinações para efetuar as atualizações. Por exemplo, para atualizar todos os aviões americanos ( USA ) com velocidade maior que 400mph:
result = db.update('plane.tbl', ['country', 'speed'], ['USA', '>400'], [1500],['range'])
Nesse exemplo nós alteramos para 1,500 mihas o alcance de todos os aviões americanos com velocidade maior que 400mph.
O critério do método update é especificado por uma lista contendo os nomes dos campos e outra lista contendo os valores a serem atualizados nesses campos. Porém, ao invés de ter que digitar todos os campos da tabela numa lista, você pode atualizar todos de uma única vez. Basta não passar a lista referente aos campos a serem atualizados, que no exemplo do parágrafo acima é ['range'], e indicar na lista de valores-atualização ( os valores que substituirão os antigos ) os valores para cada campo da tabela ( exceto o recno, que é imutável ), na mesma ordem e com o mesmo tipo. Um exemplo dessa propriedade seria:
result =db.update('plane.tbl', ['recno'], [106], ['P-47', 'USA', 347, 789,
datetime.date(1942,12,22)])
Lembre-se que a ordem e os tipos da lista de valores-atualização devem coincidir. É importante ressaltar que o campo recno é imutável, portanto não precisa ser passado nessa lista.
Você também pode usar um dicionário para atualizar uma lista. Reescrevendo o exemplo acima usando um dicionário ficaria da seguinte maneira:
result =db.update('plane.tbl', ['recno'], [106], {'name': 'P-47', 'country':'USA','speed': 347,
'range': 789, 'began_service':datetime.date(1942,12,22)})
Você também pode usar uma instância de objeto para atualizar os campos. Para cada campo que você deseje atualizar, deve haver um atributo na instância do objeto, sendo que esse atributo deve ser do valor ao qual você deseja atualizar o campo. Reescrevendo o exemplo acima usando objetos ao invés de dicionários ficaria da seguinte maneira:
class Record(object): pass
rec = Record()
rec.name ='P-47'
rec.country = 'USA'
rec.speed = 347
rec.range =789
rec.began_service =datetime.date(1942,12,22)
result = db.update('plane.tbl',['recno'], [106], rec)
O método update retorna um inteiro que indica o número de registros atualizados.
Deletar registros da tabela é muito similar ao método update usado para atualizar registros, exceto que você não deve passar uma lista contendo os nomes dos campos a serem atualizados e outra lista com os valores que atualizarão esses campos.
Como mencionado acima, para deletar um registro específico, simplesmente especifique na lista o campo 'recno' como sendo o campo a ser buscado. Então, para deletar o registro de recno 456 fazemos:
result =db.delete('plane.tbl', ['recno'],[456])
Para deletar múltiplos registros você deve especificar um critério de busca. O delete funciona da mesma maneira que o método select, só que ao invés de retornar os resultados, deleta-os. Portanto, para deletar todos aviões da Alemanha que tem o alcane menor que 800 milhas:
result =db.delete('plane.tbl', ['country', 'range'], ['Germany','<800'])
O método delete retorna um inteiro que indica o número de registros deletados.
result = db.pack('plane.tbl')
O método pack retorna um inteiro indicando o número de linhas em branco removidas.
result =db.validate('plane.tbl')
O método validate retorna uma lista contendo registros com valores errôneos. Cada registro é uma lista que contém o recno do registro, o nome do campo com valor inválido e o valor desse campo. Uma lista vazia indica que o banco de dados passou no teste.
Esse método deleta uma tabela e inclusive todo o seu conteúdo. Exemplo:
result =db.drop('plane.tbl')
O método drop retorna True se a deleção ocorrer com sucesso.
db.addFields('plane.tbl', ['bomb_load:int'],after='range')
O exemplo acima adiciona um novo campo chamado "bomb_load" com o tipo int na tabela Plane. A variável after nos permite definir onde o KirbyBase criará o novo campo. Se você não definir a variável after, o novo campo será adicionado logo após o campo recno.
O método addFields retorna True se a criação do novo campo ocorrer com sucesso.
Para remover um campo de uma tabela:
db.dropFields('plane.tbl', ['bomb_load:int'])
O exemplo acima remove um campo chamado bomb_load da tabela Plane. O método dropFields retorna True se a deleção ocorrer com sucesso.
result =db.getFieldNames('plane.tbl')
Retorna uma lista contendo os nomes dos campos da tabela Plane.
result =db.getFieldTypes('plane.tbl')
Retorna uma lista contendo os tipos dos campos.
totalRecords =db.len('./db/plane.tbl')
Retorna um inteiro que indica o número total de registros na tabela.
db.setDefaultReturnType('object')
O tipo padrão de retorno para o método select é 'list', isto é, uma lista. Mas você pode alterá-lo na própria chamada do método definindo a função returnType, mas isso pode ficar trabalhoso caso você sempre queira um tipo diferente da lista. Esse método permite você definir um outro tipo padrão de retorno do método select.
Como as tabelas do KirbyBase são simples arquivos texto, a inserção de caracteres como "\n", "\r\n" e "|" podem causar problemas na leitura do banco de dados. Tendo em vista que "\n" ( unix ) e "\r\n" ( windows ) são o delimitadores entre registros e o "|" que delimita os valores e os campos.
Mas relaxe, o KirbyBase troca esses caracteres por codificações que os tornam "menos perigosos" :)
A seguir a tabela com os valores pelo qual cada caractere-perigoso é substituido:
| Caracter de entrada | Troca feita pelo KirbyBase |
|---|---|
| \n | &linefeed; |
| \r | &carriage_return; |
| \032 | &substitute; |
| | | &pipe; |
Cada tabela do KirbyBase é um arquivo texto simples delimitado por "newlines" ( quebras de linha ) :
000006|000000|recno:int|name:str|country:str|speed:int|range:int
1|P-51|USA|403|1201
2|P-51|USA|365|888
3|Sptitfire|England|345|540
4|Oscar|Japan|361|777
5|ME-109|Germany|366|514
6|Zero|Japan|377|912
A primeira linha é cabeçalho. Cada campo é delimitado por um "|". O primeiro campo no cabeçalho indica o número de registros. Ele é incrementado pelo KirbyBase automaticamente quando novos registros são inseridos. O segundo campo é o número de campos deletados. Toda vez que um registro é deletado, esse número é incrementado. Você pode usar esse campo para criar rotinas de manutenção, tipo, rodar o método pack sempre que esse valor chegar a 5000. O terceiro campo é o campo recno, ele é adicionado automaticamente quando a tabela é criada. Os outros campos são campos definidos pelo usuário quando este criou a tabela.
Cada registro na tabela é uma linha do texto. A quebra de linha delimita os registros.
Existem dois scripts para servidores incluidos nessa distribuição:
kbthreadedserver.py - cria um servidor com múltiplas threads. Isto significa que cada cliente conecta para uma thread própria no server. A única chance de uma thread "atrasar" outra é caso ela abra a tabela para modificação ( métodos: update, delete e insert ). Mesmo nesse caso, ela só bloqueará outras thread com requisições para alterar o arquivo também. Outra thread que queira apenas fazer um select não será bloqueada. Nesse servidor, um usuário só terá que esperar pelo termino da requisição de outro usuário caso os dois estejam usando a mesma tabela. Esse servidor consegue implementar isso graças a uma lista de "locks" para as tabelas.
***Observação: Ainda que eu tenha testado o kbthreadedserver.py algumas vezes, eu ainda o considero um beta. Faça seus próprios testes antes de pô-lo em produção.
KirbyBase está licenciado sobre a Python Software Foundation License.