Clipper On Line • Ver Tópico - Harbour API SQL

Harbour API SQL

Discussão sobre Banco de Dados e RDDs para Clipper/[x]Harbour.

Moderador: Moderadores

 

Harbour API SQL

Mensagempor Ranier » 10 Set 2019 10:19

Acredito que o grande sucesso que o Clipper teve foi devido a facilidade de se programar, aliado ao fato de se ter uma api uniforme para tratar do acesso de dados, relatórios e telas.
Com a evolução da própria informática, o Clipper ficou para trás, principalmente com os erros cometidos pela CA.
Então, a grande questão para todos, que facilitaria e unificaria o desenvolvimento dos sistemas em xBase, é ter uma api uniforme e que todos a usem.
Então, teria que seria criado na minha opnião, uma API SQL, que qualquer biblioteca seguisse e que para o desenvolvedor xBase seria única, então bastaria fazer um só fonte (prg) e ele funcionaria em qualquer ambiente que o Harbour fosse portado.
Parece-em que alguém têm uma proposta chamada HDO (Acesso Universal de Dados).
O autor está chamando essa api de RDL (Replaceable Data Link), poderia ser um caminho, se todos trilhassem juntos.
email do autor: mesanonimo@gmail.com

/***
* Proyecto: Harbour Data Objects (HDO)
* Fichero: hdoRDL.c
* Descripcion: Gestion de de RDLs
* Autor: Manu Exposito 2014-19
* Fecha: 07/07/2019
*/

/*
* Notas:
* Actualmente solo tiene una DATA de solo lectura con una tabla Hash
* { NOMBRE_RDL => PUNTERO_FUNCION_METODOS }
*
* Al registrar el nodo deberian quedar resueltas las tablas virtuales de metodos
*
* Para el nuevo sistema se usara una estructura NODO -> LISTRDLNODE y NODOS -> RDLNODE
* Crear por getNode o findRDL
* Quitar METHOD( "GETPFUNCBYNAME",   TRDL_GETPFUNCBYNAME )
* Crear getHDOMethods, getSTMTMethods y getRSMethods
*/

#include "hdordl.h"

/***
* Definicion de la clase con atributos y metodos
*/

CREATE_CLASS( "TRDL", TRDL, TRDL_IVARCOUNT )
{
/* Metodos */
METHOD( "NEW",              TRDL_NEW )
METHOD( "REGISTER",         TRDL_REGISTER )
METHOD( "DEREGISTER",       TRDL_DEREGISTER )
METHOD( "LIST",             TRDL_LIST )
METHOD( "ISREGISTERED",     TRDL_ISREGISTERED )
METHOD( "GETPOS",           TRDL_GETPOS )
METHOD( "GETNAME",          TRDL_GETNAME )
}
END_CLASS

/***
* Metodo: NEW
* Uso: Constructor de la clase
* Parametros: Ninguno
* Devuelve: Self, un objeto inicializado
*/

HB_METHOD( TRDL_NEW )
{
PHB_ITEM pSelf = HB_PSELF();
PHB_ITEM pd = hdo_listRDL();

hb_arraySetForward( pSelf, IVAR_HASHRDL, pd );
HDO_ITEM_FREE( pd );

/* Devuelve SELF */
hb_itemReturnRelease( pSelf );
}

/***
* Metodo: LIST
* Uso: Consulta de RDL registrados
* Parametros: Ninguno
* Devuelve: Array con los RDLs registrados
*/

HB_METHOD( TRDL_LIST )
{
PHB_ITEM pd = HB_GETHRDL();

if( pd )
{
hb_itemReturnRelease( hb_hashGetValues( pd ) );
}
else
{
hb_reta( 0 );
}
}

/***
* Metodo: REGISTER
* Uso: Registra un RDL si no existe
* Parametros: El nombre del RDL y un puntero a la funcion del RDL
* Devuelve: Nada
*/

HB_METHOD( TRDL_REGISTER )
{
PHB_ITEM pd = HB_GETHRDL();

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );
PHB_ITEM pValue = hb_param( 2, HB_IT_STRING );

hb_hashAdd( pd, pKey, pValue );
}
}

/***
* Metodo: REGISTER
* Uso: Registra un RDL si no existe
* Parametros: El nombre del RDL y un puntero a la funcion del RDL
* Devuelve: Nada
*/

HB_METHOD( TRDL_DEREGISTER )
{
PHB_ITEM pd = HB_GETHRDL();
HB_BOOL fRet = HB_FALSE;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

fRet = hb_hashDel( pd, pKey );
}

hb_retl( fRet );
}

/***
* Metodo: ISREGISTERED
* Uso: Comprueba si un RDL esta registrado
* Parametros: Nombre del RDL
* Devuelve: Valor logico indicando si existe o no
*/

HB_METHOD( TRDL_ISREGISTERED )
{
PHB_ITEM pd = HB_GETHRDL();
HB_BOOL fRet = HB_FALSE;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

fRet = ( hb_hashGetCItemPos( pd, hb_itemGetCPtr( pKey ) ) != 0 );
}

hb_retl( fRet );
}

/***
* Metodo: GETPOS
* Uso: Comprueba la posicion que ocupa un RDL
* Parametros: Nombre del RDL
* Devuelve: La posicion o 0 encaso de no estar registrado
*/

HB_METHOD( TRDL_GETPOS )
{
PHB_ITEM pd = HB_GETHRDL();
HB_SIZE nRet = 0;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

nRet = hb_hashGetCItemPos( pd, hb_itemGetCPtr( pKey ) );
}

hb_retni( nRet );
}

/***
* Metodo: GETNAME
* Uso: Consultar el nombre de un RDL por la posicion que ocupa
* Parametros: Entero positivo
* Devuelve: El nombre del RDL si esta registrado o NULL si no lo esta
*/

HB_METHOD( TRDL_GETNAME )
{
PHB_ITEM pd = HB_GETHRDL();
PHB_ITEM pRet = NULL;

if( pd )
{
HB_USHORT nPos = hb_parni( 1 );

if( nPos >= 1 )          /* Controlar el LEN del array */
{
pRet = hb_hashGetKeyAt( pd, nPos );
}
}

if( pRet )
{
hb_itemReturn( pRet );
}
else
{
hb_retc_null();
}
}
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 80
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 09:21

Ola!
Não entendi a diferença dela para o SQLMIX.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor Ranier » 12 Set 2019 10:40

Não conheço a proposta HDO a fundo para responder 100%.
Sqlmix, pelo que sei, você tem mais conhecimentos.
Mas essa sintaxe ai:
? RDDINFO(RDDI_CONNECT,{"ODBC","Server=.\SQLEXPRESS;Driver={SQL Server};dsn=;Uid=sa;database=test;pwd=pass" },'SQLMIX' )
? RDDINFO(RDDI_EXECUTE, "DROP TABLE test")
? RDDINFO(RDDI_EXECUTE, "CREATE TABLE test (CODE char(3), DESCR char(50), VALUE int(11))")
? RDDINFO(RDDI_EXECUTE, "INSERT INTO test values ('AA','Lithuania', 3369600), ('USA', 'United States of America', 305397000)")

Esse é o problema maior, se tiver uma api unificada e util, cada rdd poderia se adaptar e cada um usaria a que achasse melhor. Exemplo:
dbQuery(), é um ótimo nome de função que é bem explicado. Executa uma "querie" que traz um dataset de retorno.
dbExecute(), executa uma "querie" que não traz um dataset de retorno.

Essas divisões, que existem, existem porque não existem uma api unificada para tudo no mundo xBase.
Se o Harbour adotasse uma api, para tudo, seria o melhor para todo o mundo xBase.
Igual foi feito no Vulkan, é uma proposta de API, cada um tem que seguir as diretrizes la, para fazerem os drivers.
Quem usa os drivers, não se importa com a especificação em si.

Se o Harbour criar uma API SQL, uma nova RDD, agora voltada para o mundo SQL, sem trazer os problemas do mundo DBF.
SQLMix, SQLRDD, poderiam adota-la. Assim, cada uma faria a sua escolha, mas teria apenas um fonte.
Não precisaria mudar no futuro, tudo o que ja foi feito, de novo, e de novo.
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 80
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 11:13

Ola!
Me desculpe, mas ainda não entendi. Essa mesma sintaxe funciona para qualquer motor SGBD.
O RDD do harbour é flexível, ao ponto de transformar a resposta em DBF, na memória, pois estamos acostumados. E os comandos são em SQL quando formos interagir com os SGBD´s e com a nossa aplicação usamos sintaxe xBase.
Esses comandos em SQL do SQLMIX, funciona com qualquer SGBD. Não precisa mudar nada quando trocar de MySQL para Firebird por exemplo.
A proposta do SQLMIX eu consigo entender. Porém essa do HDO não entendi ainda.

Eu entendo alguma coisa do RDD do Harbour, pois já li alguns códigos dele, eu não sei se vc está se referindo a isso.
                    Simple SQL Interface for Harbour

1. Introduction

   Simple SQL interface implements accessing SQL query result via RDD
interface. It is not intended to be replacement for "transparent" move of
DBFCDX application to SQL world.

   I want to discuss this in more detail. Many current RDDs for SQL servers
(ex. SQLRDD from xHarbour.com) tries to make a feeling you are working with
DBF file, but not with SQL database. SQL server does not support many
features, ex. RecNo(), deleted flag, file locks, record locks. These RDDs
are emulating these features to make feeling of DBF. Deleted() function is
emulated by creating additional table columns to store delete flag. Some
"hidden system" tables are used to register locking operations and emulate
record and file locks in DBF style. The idea of SQL query is also lost. If
you do a simple loop

dbUseArea( , "select * from my_table" )
DO WHILE ! Eof()
    somefunc( FIELD->some_sql_field )
    dbSkip()
ENDDO

RDD usualy will read SQL rows in portions, let's say 100 records per query.
So, hidden queries are generated. If you are using indexes these queries
are really complicated. Let's have index on FIELD1 + Str( FIELD2 ). A seek
to value cValue1 + Str( nValue2 ) will generate a query like:

SELECT * FROM my_table
     WHERE (FIELD1 == cValue1 and FIELD2 >= nValue2) or FIELD1 > cValue1
     ORDER BY FIELD1, FIELD2, _RECNO
     LIMIT 100

After evaluation of first 100 cached records, next query will be generated:

SELECT * FROM my_table
     WHERE (FIELD1 == cLastField1 and FIELD2 == nLastValue2 and _RECNO > nLastRecno) or
           (FIELD1 == cLastField1 and FIELD2 > nLastValue2) or
           FIELD1 > cLastValue1
     ORDER BY FIELD1, FIELD2, _RECNO
     LIMIT 100

To optimize these queries the SQL index expresion should be
"FIELD1,FIELD2,_RECNO", but not "FIELD1,FIELD2" as written in INDEX ON
command.

   "Simple SQL interface" is too long to repeat every time I want to
address this library. I'll also use acronym "SSI" to address it.

   The idea of SSI is different. It does not make hidden queries. All
queries should be made explicitly by programmer. SSI gives access to query
result via RDD interface, it does not tries to emulate DBF and be
"plug-and-play" solution for DBF to SQL migration. If you do

dbUseArea( , "select * from my_table")

all query (it could contain millions of records!) will be cached.

   The features of SSI approach are:

- It's possible to access SQL database of other applications. Other
applications usualy does not follow agreement of "plug-and-play" SQL drivers
about additional DELETED column, _RECNO in the end of index expression, etc.
Access of SQL database of other applications is sometimes not possible.

- It's query oriented. That means a simple DO WHILE ! Eof() loop will iterate
each records once and only once. This is not true for "plug-and-play" SQL
drivers, if indexing is used. Just like in the case of loop over DBF file.
It is not guaranteed that all records are included! Yes! If key value of the
first record in index is changed to be the last record in index during the
phase of record processing, DO WHILE ! Eof() loop will iterate only this
single records even if the database contains millions of records. Your sould
do FLock() on DBF to guarantee the records are not changed. Do you use FLock()
before readonly DO WHILE ! Eof() loops? :)

2. Architecture

             +-------------+
             |             |
             | SQLMIX RDD  |
             |             |
             +-------------+
                  |  ^
                  V  |
             +-------------+    +---------+
             |             |--->|         |
             | SQLBASE RDD |    |   SDD   |
             |             |<---|         |
             +-------------+    +---------+

   SQLBASE RDD implements basic functionality for accessing SQL query result
via RDD interface. This RDD could be used, if indexing of query result is not
necessary or all indexing is done by SQL server (by using ORDER BY clause).

   SQLMIX RDD implements indexing of query result. This indexing is not
related to SQL server ORDER BY clause. SQLMIX do indexing of the query on the
client side.

   SDD is acronym for Sql Database Driver. RDD is used to implement access
of different database formats like DBF, SDF, etc. SDD is used to implement
access of different SQL databases. Every SQL server (MySQL, PostgreSQL, etc.)
has a corresponding SDD. SDD driver implements a specific part of data
exchange interface between SQLBASE and SQL server.

3. Modifying database

   SSI presents a query result via RDD interface and generates no hidden
SQL queries. So, how database can be changed? Does dbAppend() and FieldPut()
works, or is it readonly SQL interface?
   dbAppend(), FieldPut() and other similiar functions work on cached query
result, i.e. query can be appended by new rows and field values can be
changed, but SQL database is not changed. dbCreate() function can also be
used to create an "empty query result" but no table is created on SQL server.
So, SSI can also be used as implementation of "array RDD".
   The programmer must call SQL command explicitly to modify SQL tables.
SSI provides a method to detect which cached rows was changed or appended.


Essa é a proposta do SQLMIX.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 11:16

Ola!
SQLMix seria outra coisa, inclusive é bem limitada, servindo basicamente para queries de consulta e nada mais.


Não. Veja nos fontes, que usamos para qualquer comando SQL.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor JoséQuintas » 12 Set 2019 12:29

hazael escreveu:É justamente isso que queria dizer, que SQLMix é muito bom, prático, mas não serve para nada além de consultas (ou relatórios).
Não tem como incluir, apagar, alterar, ou seja, não serve.


Só serve pra tirar proveito de velocidade no que SQL é bom kkkkkkk

Na prática, incluir/alterar/etc. é onde mais existe diferença entre as bases de dados, por isso complica.

Mesmo no ADO, isso tem limitações, porque os fabricantes geralmente criam coisas "usáveis", e não 100% funcionais.

Clientes.Codigo = varCodigo
Clientes.Update


Isso pode dar erro, pelo simples fato do fabricante não ter conseguido (ou não ser possível) criar algo padrão.
Se fabricante não consegue... o que dizer dos "não fabricantes".

Aí... parte-se pra uso de SQL pra tudo, que sempre funciona e pronto.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18013
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1206 vezes

Harbour API SQL

Mensagempor Ranier » 12 Set 2019 12:54

Então, é isso que acontece, confusão na mente de todo mundo. SQLmix, ainda está mesclando dois mundos DBF e SQL. Parece-me que ele usa o dataset de retorno e emula alguns comandos dbf para o usuário.
Acredito que para quem puder é melhor migrar tudo para o SQL e com uma sintaxe nova e clara, de fácil compreensão.
Isso tudo é muito teórico, claro, mas seria o melhor para o xBase.

Se todos as libs adotasse essa sintaxe, por exemplo:
res := dbQuery("SELECT * FROM Clientes ORDER BY Nome;")

Não importaria, se uso ADO, SQLMix, SQLRDD, HBDBD, teria que funcionar.

Poderia ser acrescentado parâmetros, para definir o modo de retorno, se como arrays ou hashs.
O que importa é unificar a API. Não conheço 100% da proposta do HDO e a usei apenas como exemplo, de ponto de partida.
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 80
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Harbour API SQL

Mensagempor Ranier » 12 Set 2019 13:17

Vendo esse exemplo em SQLMix:
cRdd  := RDDSETDEFAULT( "SQLMIX" )
cAttributes:="Server=;Database=127.0.0.1:c:\temp\dbafirebird.gdb;User=SYSDBA;Password=masterkey;Driver={Firebird/InterBase(r) driver};"
nConn := RDDINFO( RDDI_CONNECT,{"ODBC",cAttributes} )

IF nConn == 0
   hwg_Msginfo( "Não conectado ao servidor: " + str(RDDINFO( RDDI_ERRORNO )) + hb_eol() + RDDINFO( RDDI_ERROR ) )
   RDDSETDEFAULT( cRdd )
   RETURN
ENDIF

DBUSEAREA( .T.,"SQLMIX", "select * from produto", "xx" )


Mescla, mundo DBF com SQL, criando e ocupando WorkAreas do RDD.
Agora veja em HBDBD:

cCONN := "host='192.168.15.2' dbname='dbtests' user='postgres' password='xxxxxxx'"
hDBD := DBD_NEW(DBD_PGSQL_DRIVER)
If ( hDBD == NIL )
    hwg_Msginfo( "Sem memoria!")
    Return 1
Endif
nRET := DBD_OPEN( hDBD, sCONN )
If ( nRET != 0 )
    hwg_Msginfo( "Não conectado ao servidor: " + str(DBD_ERRORNO())
    Return nRET
Endif
hRES := DBD_QUERY( hDBD, "select * from produto" )


Nesse caso, não existe nenhum uso da RDD do Harbour, ou seja, não se têm uso de nenhum função que emule dbf.
É tudo em SQL e funções nativas da lib (HBDBD). O dataset hRES contém, todo o conjunto retornado pelo SGDB, que corresponde a instrução SQL solicitada. A partir dai, o uso dos dados, é intrínseco a HBDBD, que tem que usar funções e sintaxe própria.

hRES := DBD_QUERY( hDBD, "select * from produto" )
If ( hRES != NIL)
   hROW := DBD_FETCH_ROW( hDBD, hRES )
   While(( hROW := DBD_FETCH_ROW( hDBD, hRES ) ) != NIL )
       aROW := DBD_ROW_ARRAY( hDBD, hROW )
   Enddo
Endif
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 80
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 13:36

Ola!
Não sinta-se mal mas creio que afirmar assim com tanta convicção sem ter comprovação acaba causando confusão.

Eu hein... Tá nos exemplos, já testei n vezes, já ia sair do DBF usava o Letodb, mas tinha bugs... Ai o Elch fez o LetoDbf.
Eu não sei pq vc estão afirmando que o SQLMIX só extrai dados. Eu hein. Não vou ficar aqui evangelizando, veja vcs mesmo nos exemplos.
CRIAR, EXCLUIR, INCLUIR etc. e tals , qualquer comando SQL... Valei me... Só faltam dizer que a terra é plana!
Não entendeu o texto em inglês do Mindaugas... Ele diz que para interagir com o SGBD só com SQL.
Olha os comandos ai:

 RDDINFO(RDDI_CONNECT,{"ODBC","Server=.\SQLEXPRESS;Driver={SQL Server};dsn=;Uid=sa;database=test;pwd=pass" },'SQLMIX' )
? RDDINFO(RDDI_EXECUTE, "DROP TABLE test")
? RDDINFO(RDDI_EXECUTE, "CREATE TABLE test (CODE char(3), DESCR char(50), VALUE int(11))")
? RDDINFO(RDDI_EXECUTE, "INSERT INTO test values ('AA','Lithuania', 3369600), ('USA', 'United States of America', 305397000)")


Ele não fez e nem deseja um EMULADOR de DBF.

SDD gives you an access to query result. So, you edit query result but not database itself. Editing of database are not possible in all cases, f.e., you can not edit it if it has no primary key. So, editing put an additional restrictions on databases, or some "hidden" columns if you expect SDD to be able write to database. That was not my goal to emulate .dbf in SQL, because you'll never can emulate 100% of behavior. The more you emulate (locks, deleted records, etc) the less real SQL capabilities you have (you start to loose atomic database operations, etc). My goal was not to emulate everything and have .dbf features in SQL databases, without having SQL features.

The solution
============
I'm using functions like SQLInsert("table", {"FIELD"=>xValue, ...}) and SQLUpdate("table", {"FIELD"=>xValue, ...}, {"ID"=>xPrimaryKey}). It composes SQL query string and calls RDDINFO(RDDI_EXECUTE, cQuery). These functions are very easy to implement, ex:
------------
STATIC FUNC SQLUpdate(pcTable, phSource, pchWhere)
LOCAL aI := {}, aJ, xVal

FOR EACH xVal IN phSource
   SWITCH VALTYPE(xVal)
   CASE "C"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, SQLValueToString(xVal))) ; EXIT
   CASE "D"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, DTOS(xVal)))             ; EXIT
   CASE "N"; AADD(aI, HB_StrFormat("%s=%d", xVal:__enumKey, xVal))                ; EXIT
   CASE "T"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, LEFT(HB_TTOS(xVal), 14))); EXIT
   CASE "U"; AADD(aI, HB_StrFormat("%s=NULL", xVal:__enumKey))                ; EXIT
   END
NEXT
IF HB_IsHash(pchWhere)
   aJ := {}
   FOR EACH xVal IN pchWhere
     SWITCH VALTYPE(xVal)
     CASE "C"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, SQLValueToString(xVal))) ; EXIT
     CASE "D"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, DTOS(xVal)))             ; EXIT
     CASE "N"; AADD(aJ, HB_StrFormat("%s=%d", xVal:__enumKey, xVal))                  ; EXIT
     CASE "T"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, LEFT(HB_TTOS(xVal), 14))); EXIT
     CASE "U"; AADD(aJ, HB_StrFormat("%s IS NULL", xVal:__enumKey))                  ; EXIT
     END
   NEXT
   pchWhere := join(aJ, " AND ")
ENDIF
RETURN RDDINFO(RDDI_EXECUTE, HB_StrFormat("UPDATE %s SET %s WHERE %s", pcTable, join(aI, ","), pchWhere))
------------

But I've never tried to upload these to SVN, because these functions are also limited. I'm also not sure that people likes storing record in hash. It also a separate thing, it is not related to SDD.

Regards,
Mindaugas


Apenas a QUERY retorna em um padrão DBF, mas para para atualizar o SGDB vc usa: "RDDINFO(RDDI_EXECUTE,comando SQL"
Não entenderam até hoje o conceito.
Lá no passado quando testei funcionou. Criei, incluir, alterei... usando RDDINFO(RDDI_EXECUTE...

Exemplo de uso do povo da Minigui!
* RDD SQL DEMO
* Based on Harbour Compiler Contrib Samples
* Adapted for HMG by Roberto Lopez - 2009

* Modified 2009.11.13 by Marek Olszewski "MOL" for testing Record Locking with MySQL

#include "minigui.ch"
#include "dbinfo.ch"
#include "error.ch"

#define DBI_QUERY             1001

#define RDDI_CONNECT          1001
#define RDDI_DISCONNECT       1002
#define RDDI_EXECUTE          1003
#define RDDI_ERROR            1004
#define RDDI_ERRORNO          1005
#define RDDI_NEWID            1006
#define RDDI_AFFECTEDROWS     1007
#define RDDI_QUERY            1008

REQUEST SDDMY, SQLMIX

Function Main
LOCAL hConn

   RDDSETDEFAULT("SQLMIX")

   IF RDDINFO(RDDI_CONNECT, {"MYSQL", "localhost", "root","123", "TEST"}) == 0
      MSGSTOP("Unable connect to the server")
      RETURN
   ENDIF

   CreateTable()

   // it's required to use record locking to set autocommit to 0!!!!
   RDDINFO(RDDI_EXECUTE,"SET AUTOCOMMIT = 0")
   DBUSEAREA( .T.,, "SELECT * FROM country", "country" )

   DEFINE WINDOW MAIN ;
      ROW 0 ;
      COL 0 ;
      WIDTH 600 ;
      HEIGHT 500 ;
      TITLE "RDD SQL - TESTING ROW LOCKING MODIFIED BY 'MOL" ;
      WINDOWTYPE MAIN 

      DEFINE BROWSE browse1
         ROW       40
         COL       10
         WIDTH      470         
         HEIGHT      330
         HEADERS    {'Code','Name','Residents'}
         WIDTHS      {135,155,135}
         WORKAREA   COUNTRY
         FIELDS      {'Country->Code','Country->Name','Country->Residents'}
      END BROWSE
      DEFINE BUTTON B1
         ROW 10
         COL 10
         WIDTH 100
         CAPTION "REFRESH"
         ACTION REFRESH_BROWSE()
      END BUTTON

      DEFINE BUTTON B_LOCK
         ROW 10
         COL 120
         WIDTH 100
         CAPTION "LOCK 'POL'"
         ACTION LOCK_RECORD()
      END BUTTON
      
      DEFINE BUTTON RELEASE_LOCK
         ROW 10
         COL 230
         WIDTH 100
         CAPTION "RELEASE LOCK"
         ACTION RELEASE_lock()
      END BUTTON
      DEFINE BUTTON MODIFY_POL
         ROW 10
         COL 340
         WIDTH 100
         CAPTION "MODIFY 'POL'"
         ACTION CHANGE_RECORD()
         
      END BUTTON
         
   END WINDOW

   MAIN.Center

   MAIN.Activate

Return

************************
STATIC PROC CreateTable()
   ? RDDINFO(RDDI_EXECUTE, "DROP TABLE country")
   ? RDDINFO(RDDI_EXECUTE, "CREATE TABLE country (CODE char(3), NAME char(50), RESIDENTS int(11))")
   ? RDDINFO(RDDI_EXECUTE, "INSERT INTO country values ('LTU', 'Lithuania', 3369600), ('USA', 'United States of America', 305397000), ('POR', 'Portugal', 10617600), ('POL', 'Poland', 38115967), ('AUS', 'Australia', 21446187), ('FRA', 'France', 64473140), ('RUS', 'Russia', 141900000)")
RETURN

************************
function REFRESH_BROWSE
   DBUSEAREA( .F.,, "SELECT * FROM country", "country" )
   MAIN.BROWSE1.REFRESH
   MAIN.BROWSE1.SETFOCUS
return
************************
PROC RDDSYS();  RETURN
************************
FUNCTION LOCK_RECORD
   IF RDDINFO(RDDI_EXECUTE,"SELECT * FROM country WHERE CODE='POL' FOR UPDATE")
      MsgBox("Locked...")
   ELSE
      MsgBox("it's not possible to lock record...")
   ENDIF
return
************************
FUNCTION RELEASE_LOCK
   RDDINFO(RDDI_EXECUTE,"COMMIT")
return
************************
FUNCTION CHANGE_RECORD
   local cNewName := ""

   RDDINFO(RDDI_EXECUTE,"COMMIT;")
   WAIT WINDOW "WAITING FOR LOCK RECORD IN DATABASE..."
   IF RDDINFO(RDDI_EXECUTE,"SELECT * FROM country WHERE CODE='POL' FOR UPDATE")
      
      cNewName := AllTrim ( InputBox ( "Enter new name for CODE='POL'" , "Enter new name...") )
      RDDINFO(RDDI_EXECUTE,"update country set name = '" + cNewName +"' where CODE ='POL'")
      RDDINFO(RDDI_EXECUTE,"COMMIT")
   ELSE
      MSGSTOP("I can't lock record for CODE='POL'")
   ENDIF
   WAIT CLEAR
   REFRESH_BROWSE()
return
************************
function xtoC
   param   xValue
   local ret
   
   if type("xValue") == "D"
      ret := dtoc(xValue)
   elseif type("xValue") == "N"
      ret := str(xValue)
   elseif type("xValue") == "L"
      ret := if(xValue,"True","false")
   elseif type("xValue") = "C"
      ret := xValue
   else
      ret := ""
   endif
return ret


Quem tem que testar é o interessado, quando eu precisei testei e funcionou, agora testem e mostrem que não faz, porque tudo é trabalho.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor JoséQuintas » 12 Set 2019 13:49

Ranier escreveu:Se todos as libs adotasse essa sintaxe, por exemplo:


Ao mesmo tempo, faz sentido, e não faz sentido....
Mais padronizado que ODBC e ADO impossível.
ADO só tem pra Windows, mas ODBC tem pra Linux também.

Se já existe padrão ADO e padrão ODBC.... um novo padrão vai ser algo não padrão kkkkk

E voltamos à estaca zero, qualquer novo padrão não vai ser padrão enquanto não for seguido.

Esse é um grande problema no xbase.....
Todo mundo à espera de um padrão que não existe...
E conforme aparece alguma coisa, se torna fora do padrão, e lá se vão mais usuários pra fora do padrão.

Pra mim não tem que inventar um novo padrão, é seguir algum dos que já existem, que acabam virando padrão.

Regra básica:
Se nem a porr. do compilador C, não seguem a padronização, o que dizer de acesso a base de dados?
Quanto mais opções no Harbour pior !!!! infelizmente.

Conclusão:
Não existe padrão.
Não tem como definir um padrão.
Mas... o SQLMIX não é um padrão?
Se não serve pra padrão.... então ninguém aceita um padrão, vamos perder tempo definindo padrão que não será usado?
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18013
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1206 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 13:51

Ola!
Só serve pra tirar proveito de velocidade no que SQL é bom kkkkkkk

Na prática, incluir/alterar/etc. é onde mais existe diferença entre as bases de dados, por isso complica.

Quintas já coloquei n vezes aqui no forum... Mas no seu pensamento só existe ADO, infelizmente existe pessoas que só usam LINUX e o pessoal do Harbour os mestre tem como padrão o LINUX como SO.
Se é melhor se é pior eu não sei. Eu sei que podemos incluir, excluir, etc.. usando SQLMIX.
cQuery := "bla, bla, bla..."

RDDINFO(RDDI_EXECUTE, "update" + cQuery )
cQuery := "bla, bla, bla..."
RDDINFO(RDDI_EXECUTE, "insert" + cQuery )

https://hmgthinking.wordpress.com/2015/07/18/23/
V. YES!

Yes indeed: The remedy was worse than the disease, but not for technical reasons, but, personal ones.

I’ve successfully ported one module of the app to MySql, using SQLMIX RDD.

It worked perfect (technically speaking, at least) but something bothered me…

In fact… two things…

I love the standalone/portable EXEs: it is one of the sexiest features on Harbour, but MySql requires a dll.
SQL itself: SQL is great… I know… You can do everything with it. The problem is that I don’t feel comfortable with it. My brain, my heart and the rest of my body started to tell me: ‘Hey… SQL is not as easy, intuitive and enjoyable as xBase is… please… STOP THIS!

Then… I’ve done it… I’ve stopped the migration process and started to think again.

VI. A FUNNY THING HAPPENED IN THE WAY TO… CLIENT/SERVER

And I’ve remembered a new Harbour toy called NETIO.

It, basically, could allow you to use your actual code, designed for local access, on a LAN or even through the Internet with minor changes.


Ele achou o NetIO eu achei o LetoDbf, estamos felizes com DBF.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 14:16

Ola!
Mas... o SQLMIX não é um padrão?

Sim lógico que é. Qual é o comando novo ? São comandos padrões do xBase e dos SGBDs -> SQL.
O problema é a adoção e pessoas para ensinar.

Tem outra pessoa muito gabaritada que fez um RDD "pra tudo" e o povo nem comentou, agora ele já está criando um drive do harbour para MongoDB.

Teo Fonrouge

Harbour OORDB is a Object Oriented Relational Database
https://github.com/tfonrouge/oordb
https://medium.com/harbour-magazine/interview-with-teo-fonrouge-529a1a167f7b

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor Ranier » 12 Set 2019 14:23

"Ele achou o NetIO eu achei o LetoDbf, estamos felizes com DBF."
Sim, muito bom. Eu entendo as dores da mudança. Passei por isso faz tempo.
Mas no meu entendimento, dbf não é o futuro, infelizmente. Claro que vai ter sempre algum projeto usando.
Ele foi o melhor para isso por muito tempo.
Agora um projeto sério de ERP, que precise escalar, ir pra Web, mobile, BI, e acrescente todas as tecnologias novas por ai, vai precisar de um SGDB, não tenho dúvida.

"Agora, tem outra questão, o melhor mesmo é acesso nativo, direto, e se alguém conseguir fazer rotinas de acesso direto que não mudem de acordo com o tipo de SQL em uso, seria o ideal, mas teria que ser muito, muito simples e ao mesmo tempo não faltar nenhuma possibilidade ou recurso. Tipo como fucniona com JDBC, inclusive essa é uma proposta antiga do Viktor."
Acredito que consegui com o HDBD, nativo e direto, multibanco, multiplataforma.
Sem ODBC.
Agora, com ele a mudança seria grande, realmente.
Browse somente em arrays.
Sem replace, ou append blank.
Mudou algo no banco, nova consulta, mas é assim pra todo mundo que usa SQL.
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 80
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 14:30

Ola!
Agora, tem outra questão, o melhor mesmo é acesso nativo, direto, e se alguém conseguir fazer rotinas de acesso direto que não mudem de acordo com o tipo de SQL em


A melhor proposta que já vi, foi o SQLMIX. É padronizado para o programador xBase. Traz o resultado em DBF, isso é fantástico, já poupa centenas de linhas.
As rotinas de GRUD, fazemos usando SQL que é padrão. Não sei onde está a falta de padrão.
Eu sei que tem tanto trabalho pronto, pq para quem ama PgSQL, vai querer usar o rdd do Rodrigo Moreno, e por ai vai...

#require "hbpgsql"

PROCEDURE Main( cHost, cDatabase, cUser, cPass )

   LOCAL conn, res, x, y, pFile

   CLS

   conn := PQsetdbLogin( cHost, "5432", , , cDatabase, cUser, cPass )
   ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn )

   conn := PQconnectdb( ;
      "dbname = '" + hb_defaultValue( cDatabase, "postgres" ) + "' " + ;
      "host = '" + hb_defaultValue( cHost, "localhost" ) + "' " + ;
      "user = '" + hb_defaultValue( cUser, hb_UserName() ) + "' " + ;
      "password = '" + hb_defaultValue( cPass, "" ) + "' " + ;
      "port = 5432" )

   ? PQstatus( conn ), PQerrorMessage( conn )

   IF PQstatus( conn ) != CONNECTION_OK
      RETURN
   ENDIF

   ? "Blocking:", PQisnonblocking( conn ), PQsetnonblocking( conn, .T. ), PQisnonblocking( conn )

   pFile := PQtracecreate( hb_DirBase() + "trace.log" )
   PQtrace( conn, pFile )

   ? "Verbose:", PQsetErrorVerbosity( conn, 2 )

   ? "Protocol:", PQprotocolVersion( conn )
   ? "Server version:", PQserverVersion( conn )
   ? "Client encoding (old):", pg_encoding_to_char( PQclientEncoding( conn ) )
   ? "Set client encoding:", PQsetClientEncoding( conn, "UTF-8" )
   ? "Client encoding (new):", pg_encoding_to_char( PQclientEncoding( conn ) )

   ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn )

   res := PQexec( conn, "drop table products" )
   ? PQresultStatus( res ), PQresultErrorMessage( res )
   res := NIL

   res := PQexec( conn, "create table products ( product_no numeric(10), name varchar(20), price numeric(10,2) )" )
   ? PQresultStatus( res ), PQresultErrorMessage( res )

   res := PQexecParams( conn, "insert into products( product_no, name, price ) values ($1, $2, $3)", { "2", "bread", "10.95" } )
   ? "Oid Row:", PQoidValue( res ), PQoidStatus( res )

   IF PQresultStatus( res ) != PGRES_COMMAND_OK
      ? PQresultStatus( res ), PQresultErrorMessage( res )
   ENDIF

   res := PQexec( conn, 'select price, name, product_no as "produto" from products' )

   IF PQresultStatus( res ) != PGRES_TUPLES_OK
      ? PQresultStatus( res ), PQresultErrorMessage( res )
   ENDIF

   ? "Binary:", PQbinaryTuples( res )
   ? "Rows:", PQntuples( res )
   ? "Cols:", PQnfields( res )
   ? PQfname( res, 1 ), PQftable( res, 1 ), PQftype( res, 1 ), PQfnumber( res, "name" ), PQfmod( res, 1 ), PQfsize( res, 1 ), PQgetisnull( res, 1, 1 )

   FOR EACH x IN PQmetadata( res )
      ? "Line 1:", ""
      FOR y := 1 TO 6
         ?? x[ y ], ",", ""
      NEXT
   NEXT

   ? PQfcount( res )

   ? PQlastrec( res )

   ? PQgetvalue( res, 1, 2 )

   ? "Large Objects, always should be in a transaction..."

   PQexec( conn, "begin" )

   ? ( x := lo_import( conn, __FILE__ ) )
   ? lo_export( conn, x, hb_FNameExtSet( __FILE__, ".new" ) )
   ? lo_unlink( conn, x )

   PQexec( conn, "commit" )

   PQuntrace( conn )

   RETURN


Olhem a dificuldade de "labutar" com o resultado. Vem um texto enorme separado por vírgula.
Faço votos que achei coisa melhor, ou que criem coisa melhor, só estou tentando melhorar e imagem do SQLMIX e outros que estão na pasta contrib do Harbour.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Harbour API SQL

Mensagempor Itamar M. Lins Jr. » 12 Set 2019 14:39

Ola!

Agora, com ele a mudança seria grande, realmente.
Browse somente em arrays.
Sem replace, ou append blank.
Mudou algo no banco, nova consulta, mas é assim pra todo mundo que usa SQL.


Já é assim com SQLMIX. Traz a ARRAY em DBF, não tem nada salvo no HD. Depois que mexer, tem que salvar via comando SQL assim como se faz usando ADO.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes




Retornar para Banco de Dados

Quem está online

Usuários vendo este fórum: Nenhum usuário registrado online e 12 visitantes


Ola Amigo, espero que meu site e forum tem lhe beneficiado, com exemplos e dicas de programacao.
Entao divulgue o link da Doacao abaixo para seus amigos e redes sociais ou faça uma doacao para o site forum...
MUITO OBRIGADO PELA SUA DOACAO!
Faça uma doação para o forum
cron
v
Olá visitante, seja bem-vindo ao Fórum Clipper On Line!
Efetue o seu login ou faça o seu Registro