diff --git a/JavaSybaseLink/dist/JavaSybaseLink.jar b/JavaSybaseLink/dist/JavaSybaseLink.jar index 56215ad..14195e8 100644 Binary files a/JavaSybaseLink/dist/JavaSybaseLink.jar and b/JavaSybaseLink/dist/JavaSybaseLink.jar differ diff --git a/JavaSybaseLink/manifest.mf b/JavaSybaseLink/manifest.mf index 328e8e5..eea5ca7 100644 --- a/JavaSybaseLink/manifest.mf +++ b/JavaSybaseLink/manifest.mf @@ -1,3 +1,3 @@ Manifest-Version: 1.0 +Main-Class: Main X-COMMENT: Main-Class will be added automatically by build - diff --git a/JavaSybaseLink/src/Main.java b/JavaSybaseLink/src/Main.java index 101f423..f81523d 100644 --- a/JavaSybaseLink/src/Main.java +++ b/JavaSybaseLink/src/Main.java @@ -21,25 +21,32 @@ public static void main(String[] args) { Main m; String pw = ""; - if (args.length != 5 && args.length != 4) + String encoding = null; + if (args.length < 4 || args.length > 6) { - System.err.println("Expecting the arguments: host, port, dbname, username, password"); + System.err.println("Expecting the arguments: host, port, dbname, username, [password], [encoding]"); System.exit(1); } - if (args.length == 5) + if (args.length >= 5) pw = args[4]; + if (args.length == 6) + encoding = args[5]; - m = new Main(args[0], Integer.parseInt(args[1]), args[2], args[3], pw); + m = new Main(args[0], Integer.parseInt(args[1]), args[2], args[3], pw, encoding); } public Main(String host, Integer port, String dbname, String username, String password) { + this(host, port, dbname, username, password, null); + } + + public Main(String host, Integer port, String dbname, String username, String password, String encoding) { this.host = host; this.port = port; this.dbname = dbname; this.username = username; this.password = password; - input = new StdInputReader(); + input = new StdInputReader(encoding); input.addListener(this); MyProperties props = new MyProperties("sybaseConfig.properties"); diff --git a/JavaSybaseLink/src/StdInputReader.java b/JavaSybaseLink/src/StdInputReader.java index b99bf3e..c0d2f86 100644 --- a/JavaSybaseLink/src/StdInputReader.java +++ b/JavaSybaseLink/src/StdInputReader.java @@ -14,10 +14,23 @@ public class StdInputReader { private List listeners = new ArrayList(); - private BufferedReader inputBuffer = new BufferedReader(new InputStreamReader(System.in)); + private BufferedReader inputBuffer; public StdInputReader() { + this(null); + } + public StdInputReader(String encoding) { + try { + if (encoding != null && !encoding.isEmpty()) { + inputBuffer = new BufferedReader(new InputStreamReader(System.in, encoding)); + } else { + inputBuffer = new BufferedReader(new InputStreamReader(System.in)); + } + } catch (Exception e) { + System.err.println("Error setting encoding '" + encoding + "', falling back to default: " + e.getMessage()); + inputBuffer = new BufferedReader(new InputStreamReader(System.in)); + } } public void startReadLoop() diff --git a/README.md b/README.md index e4e31bb..8c6d1f4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ new Sybase(host: string, port: int, dbName: string, username: string, password: Where the SybaseOptions interface includes: ``` SybaseOptions { - encoding: string, // defaults to "utf8" + encoding: string, // Node.js IPC encoding, defaults to "utf8" + javaEncoding: string, // Java database communication encoding, defaults to null (uses system default) extraLogs: boolean // defaults to false } ``` @@ -71,4 +72,37 @@ var logTiming = true, The java Bridge now optionally looks for a "sybaseConfig.properties" file in which you can configure jconnect properties to be included in the connection. This should allow setting properties like: ```properties ENCRYPT_PASSWORD=true -``` \ No newline at end of file +``` + +### Character Encoding Support + +For databases using legacy character encodings (like Windows-1252), you can specify separate encoding configurations: + +```javascript +var Sybase = require('sybase'), + db = new Sybase('host', port, 'dbName', 'username', 'pw', false, null, { + encoding: 'latin1', // Node.js IPC encoding + javaEncoding: 'Cp1252', // Java database communication encoding + extraLogs: false + }); + +db.connect(function (err) { + if (err) return console.log(err); + + // Now queries with special characters (accents, cedillas, etc.) should work correctly + db.query('SELECT * FROM usuarios WHERE nome = "José"', function (err, data) { + if (err) console.log(err); + console.log(data); + db.disconnect(); + }); +}); +``` + +**Encoding Parameters:** +- `encoding`: Controls Node.js stdin/stdout encoding for communication with the Java process +- `javaEncoding`: Sets the Java system encoding (`-Dfile.encoding`) for database communication + +Common encoding combinations: +- **Windows-1252 databases**: `encoding: 'latin1', javaEncoding: 'Cp1252'` +- **ISO-8859-1 databases**: `encoding: 'latin1', javaEncoding: 'ISO-8859-1'` +- **UTF-8 databases** (default): `encoding: 'utf8'` (javaEncoding not needed) \ No newline at end of file diff --git a/src/SybaseDB.js b/src/SybaseDB.js index 5b825ea..938c454 100644 --- a/src/SybaseDB.js +++ b/src/SybaseDB.js @@ -4,7 +4,7 @@ var fs = require("fs"); var path = require("path"); -function Sybase(host, port, dbname, username, password, logTiming, pathToJavaBridge, { encoding = "utf8", extraLogs = false } = {}) +function Sybase(host, port, dbname, username, password, logTiming, pathToJavaBridge, { encoding = "utf8", javaEncoding = null, extraLogs = false } = {}) { this.connected = false; this.host = host; @@ -14,6 +14,7 @@ function Sybase(host, port, dbname, username, password, logTiming, pathToJavaBri this.password = password; this.logTiming = (logTiming == true); this.encoding = encoding; + this.javaEncoding = javaEncoding; this.extraLogs = extraLogs; this.pathToJavaBridge = pathToJavaBridge; @@ -38,7 +39,15 @@ Sybase.prototype.log = function(msg) Sybase.prototype.connect = function(callback) { var that = this; - this.javaDB = spawn('java',["-jar",this.pathToJavaBridge, this.host, this.port, this.dbname, this.username, this.password]); + var javaArgs = ["-jar", this.pathToJavaBridge, this.host, this.port, this.dbname, this.username, this.password]; + + // Add Java encoding parameter if specified + if (this.javaEncoding) { + javaArgs.splice(1, 0, "-Dfile.encoding=" + this.javaEncoding); + javaArgs.push(this.javaEncoding); // Pass encoding as additional parameter to Java app + } + + this.javaDB = spawn('java', javaArgs); var hrstart = process.hrtime(); this.javaDB.stdout.once("data", function(data) { diff --git a/test/test_encoding.js b/test/test_encoding.js new file mode 100644 index 0000000..3625a60 --- /dev/null +++ b/test/test_encoding.js @@ -0,0 +1,98 @@ +var expect = require("chai").expect; +var Sybase = require("../src/SybaseDB.js"); + +describe("Character Encoding Support", function() { + + describe("Constructor Compatibility", function() { + + it("Should support backward compatibility without encoding parameters", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password'); + + expect(db.encoding).to.equal('utf8'); + expect(db.javaEncoding).to.equal(null); + expect(db.extraLogs).to.equal(false); + }); + + it("Should support legacy constructor with logTiming", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password', true); + + expect(db.logTiming).to.equal(true); + expect(db.encoding).to.equal('utf8'); + expect(db.javaEncoding).to.equal(null); + }); + + it("Should accept encoding parameter only", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password', false, null, { + encoding: 'latin1' + }); + + expect(db.encoding).to.equal('latin1'); + expect(db.javaEncoding).to.equal(null); + expect(db.extraLogs).to.equal(false); + }); + + it("Should accept javaEncoding parameter only", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password', false, null, { + javaEncoding: 'Cp1252' + }); + + expect(db.encoding).to.equal('utf8'); // default + expect(db.javaEncoding).to.equal('Cp1252'); + expect(db.extraLogs).to.equal(false); + }); + + it("Should accept both encoding parameters (main feature)", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password', false, null, { + encoding: 'latin1', + javaEncoding: 'Cp1252', + extraLogs: true + }); + + expect(db.encoding).to.equal('latin1'); + expect(db.javaEncoding).to.equal('Cp1252'); + expect(db.extraLogs).to.equal(true); + }); + + it("Should preserve all other constructor parameters", function() { + var customJarPath = './custom/path/JavaSybaseLink.jar'; + var db = new Sybase('testhost', 9999, 'testdb', 'testuser', 'testpass', true, customJarPath, { + encoding: 'latin1', + javaEncoding: 'Cp1252' + }); + + expect(db.host).to.equal('testhost'); + expect(db.port).to.equal(9999); + expect(db.dbname).to.equal('testdb'); + expect(db.username).to.equal('testuser'); + expect(db.password).to.equal('testpass'); + expect(db.logTiming).to.equal(true); + expect(db.pathToJavaBridge).to.equal(customJarPath); + expect(db.encoding).to.equal('latin1'); + expect(db.javaEncoding).to.equal('Cp1252'); + }); + }); + + describe("Java Arguments Generation", function() { + + it("Should not modify Java args when javaEncoding is null", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password'); + + // We can't easily test the exact spawn arguments without mocking, + // but we can verify the object properties are set correctly + expect(db.javaEncoding).to.equal(null); + }); + + it("Should prepare for Java encoding when javaEncoding is set", function() { + var db = new Sybase('localhost', 5000, 'test', 'user', 'password', false, null, { + javaEncoding: 'Cp1252' + }); + + expect(db.javaEncoding).to.equal('Cp1252'); + // The actual spawn args modification happens in connect() method + // and would require a real database connection to test fully + }); + }); + + // Note: Actual database connection tests would require a real Sybase instance + // and are not included here to keep tests runnable without external dependencies +}); \ No newline at end of file